最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringSecurity整合jwt權(quán)限認(rèn)證的全流程講解

 更新時(shí)間:2021年06月23日 12:09:49   作者:小樓夜聽雨QAQ  
這篇文章主要介紹了SpringSecurity整合jwt權(quán)限認(rèn)證的全流程講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

JWT

本文代碼截取自實(shí)際項(xiàng)目。

jwt(Json Web Token),一個(gè)token,令牌。

簡(jiǎn)單流程:

用戶登錄成功后,后端返回一個(gè)token,也就是頒發(fā)給用戶一個(gè)憑證。之后每一次訪問,前端都需要攜帶這個(gè)token,后端通過token來解析出當(dāng)前訪問對(duì)象。

優(yōu)點(diǎn)

1、一定程度上解放了后端,后端不需要再記錄當(dāng)前用戶是誰,不需要再維護(hù)一個(gè)session,節(jié)省了開銷。

2、session依賴于cookie,某些場(chǎng)合cookie是用不了的,比如用戶瀏覽器cookie被禁用、移動(dòng)端無法存儲(chǔ)cookie等。

缺點(diǎn)

1、既然服務(wù)器通過token就可以知道當(dāng)前用戶是誰,說明其中包含了用戶信息,有一定的泄露風(fēng)險(xiǎn)。

2、因?yàn)槭菬o狀態(tài)的,服務(wù)器不維持會(huì)話,那么每一次請(qǐng)求都會(huì)重新去數(shù)據(jù)庫(kù)讀取權(quán)限信息,性能有一定影響。

(如果想提高性能,可以將權(quán)限數(shù)據(jù)存到redis,但是如果用redis,就已經(jīng)失去了jwt的優(yōu)點(diǎn),直接用普通的token+redis即可)

3、只能校驗(yàn)token是否正確,通過設(shè)定過期時(shí)間來確定其有效性,不可以手動(dòng)注銷。

先說怎么做,再說為什么。

代碼

依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <security.version>2.3.3.RELEASE</security.version>
        </dependency>
 
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <jwt.version>0.9.1</jwt.version>
        </dependency>

jwt工具類

public class JwtTokenUtils implements Serializable { 
    private static final long serialVersionUID = -3369436201465044824L; 
    //生成token
    public static String createToken(String username) {
        return Jwts.builder().setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis()+ Constants.Jwt.EXPIRATION))
                .signWith(SignatureAlgorithm.HS512, Constants.Jwt.KEY).compressWith(CompressionCodecs.GZIP).compact();
    }
 
    //獲取用戶名
    public static String getUserName(String token) {
        return Jwts.parser().setSigningKey(Constants.Jwt.KEY).parseClaimsJws(token).getBody().getSubject();
    }
}

涉及常量

public interface Constants {
    public interface Jwt{
        /**
         * 密鑰
         */
        String KEY = "123123";
        /**
         * 過期時(shí)間
         */
        long EXPIRATION = 7200000;
        /**
         * 請(qǐng)求頭
         */
        String TOKEN_HEAD = "Authorization";
    }
}

實(shí)體類和Service

為了減少對(duì)實(shí)體類的入侵,我又定義了一個(gè)對(duì)象

原實(shí)體類

/**
 * 用戶信息
 *
 */
@Data
@TableName("tb_user_info")
public class UserInfo implements Serializable {
	private static final long serialVersionUID = 1L;
 
	/**
	 * 主鍵id
	 */
	@TableId(type = IdType.AUTO)
	private Integer id;
	/**
	 * 登陸賬號(hào)
	 */
	private String loginName;
	/**
	 * 顯示名
	 */
	private String dispName;
	/**
	 * 密碼
	 */
	private String password;
	/**
	 * 狀態(tài),1正常,2禁用
	 */
	private Byte status;
	/**
	 * 創(chuàng)建人
	 */
	@TableField(fill = FieldFill.INSERT)
	private Integer createBy;
	/**
	 * 更新人
	 */
	@TableField(fill = FieldFill.INSERT_UPDATE)
	private Integer updateBy;
	/**
	 * 創(chuàng)建時(shí)間
	 */
	@TableField(fill = FieldFill.INSERT)
	private Date createTime;
	/**
	 * 更新時(shí)間
	 */
	@TableField(fill = FieldFill.INSERT_UPDATE)
	private Date updateTime;
	/**
	 * 1正常, 0 刪除
	 */
	@TableLogic
	private Byte deleted;
}

定義對(duì)象

@Data
public class UserSecurity implements UserDetails {
    private static final long serialVersionUID = -6777760550924505136L;
    private UserInfo userInfo;
    private List<String> permissionValues;
    public UserSecurity(UserInfo userInfo) {
        this.userInfo = userInfo;
    }
 
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for(String permissionValue : permissionValues) {
            if(StringUtils.isEmpty(permissionValue)) continue;
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }
 
        return authorities;
    }
 
    @Override
    public String getPassword() {
        return userInfo.getPassword();
    }
 
    @Override
    public String getUsername() {
        return userInfo.getLoginName();
    }
 
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
 
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
 
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
 
    @Override
    public boolean isEnabled() {
        return true;
    }
}

service

@Service
public class UserInfoServiceImpl implements  UserInfoService, UserDetailsService {
 
    @Autowired
    UserInfoMapper userInfoMapper;
 
    @Autowired
    PermissionService permissionService;
 
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("login_name", username);
        UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
        List<String> rights = permissionService.listPermissions(username);
        UserSecurity userSecurity = new UserSecurity(userInfo);
        userSecurity.setPermissionValues(rights);
        return userSecurity;
    }
}

配置類

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    UserInfoServiceImpl userInfoService;
 
    @Autowired
    JwtAuthorizationTokenFilter jwtAuthorizationTokenFilter;
 
    @Autowired
    TokenLoginFilter tokenLoginFilter;
 
    @Autowired
    JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
 
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userInfoService).passwordEncoder(passwordEncoderBean());
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and().csrf().disable()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/hello").permitAll()
                .antMatchers("/swagger-ui.html").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/v2/*").permitAll()
                .antMatchers("/csrf").permitAll()
                .antMatchers("/doc.html").permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .addFilterAt(tokenLoginFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterAfter(jwtAuthorizationTokenFilter, TokenLoginFilter.class).httpBasic()
                ;
    }
 
    @Bean
    public PasswordEncoder passwordEncoderBean() {
        return new BCryptPasswordEncoder();
    }
 
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

過濾器

一個(gè)負(fù)責(zé)登錄

一個(gè)負(fù)責(zé)鑒權(quán)

@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
 
    @Autowired
    PermissionService permissionService;
 
    @Autowired
    UserInfoServiceImpl userDetailsService;
 
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //獲取當(dāng)前認(rèn)證成功用戶權(quán)限信息
        UsernamePasswordAuthenticationToken authRequest = getAuthentication(request);
        //判斷如果有權(quán)限信息,放到權(quán)限上下文中
        if (authRequest != null) {
            SecurityContextHolder.getContext().setAuthentication(authRequest);
        }
 
        chain.doFilter(request, response);
 
    }
 
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        //從header獲取token
        String token = request.getHeader(Constants.Jwt.TOKEN_HEAD);
        if (token != null) {
            //從token獲取用戶名
            String loginName = JwtTokenUtils.getUserName(token);
            //獲取權(quán)限列表
            UserInfo userInfo = userDetailsService.selectByLoginName(loginName);
            List<String> permissions = permissionService.listPermissions(loginName);
            Collection<GrantedAuthority> authority = new ArrayList<>();
            for (String permissionValue : permissions) {
                SimpleGrantedAuthority auth = new SimpleGrantedAuthority(permissionValue);
                authority.add(auth);
            }
            return new UsernamePasswordAuthenticationToken(userInfo, token, authority);
        }
        return null;
    }
}
Component
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
 
    public TokenLoginFilter() {
        this.setPostOnly(false);
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
    }
 
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //獲取表單提交數(shù)據(jù)
        try {
            UserInfo user = new ObjectMapper().readValue(request.getInputStream(), UserInfo.class);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getLoginName(), user.getPassword(),null);
            return super.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
 
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        UserSecurity userSecurity = (UserSecurity) authResult.getPrincipal();
        String token = JwtTokenUtils.createToken(userSecurity.getUsername());
        ResponseUtils.out(response, R.ok(token));
    }
 
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        ResponseUtils.out(response, R.fail(ServiceError.LOGIN_FAIL));
    }
 
    @Autowired
    @Override
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }
}

異常處理

登陸失敗異常處理,自定義返回類型

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        ResponseUtils.out(response, R.fail(ServiceError.AUTHENTICATION_FAIL));
    }
}

至于權(quán)限和角色,得看具體的業(yè)務(wù),我這邊就不貼出來了。

流程分析

總體分析

代碼肯定不是憑空出來的,必定有章可循。

首先看網(wǎng)上找的這個(gè)圖:

最前面兩個(gè)過濾器:密碼檢驗(yàn)、權(quán)限認(rèn)證。(順序很重要,鑒權(quán)之前總得給人家輸入賬號(hào)密碼的機(jī)會(huì)吧)

想一下傳統(tǒng)的session會(huì)話模式,登錄成功后,服務(wù)器維護(hù)了session,瀏覽器得cookie里存放了sessionId,用戶訪問的時(shí)候?yàn)g覽器自動(dòng)帶上sessionId。

放在以前,服務(wù)器端的工作都是是框架幫我們做了(原生session來自于tomcat)。

換成jwt后,需要前端自己去存儲(chǔ)token,后臺(tái)自己來解析token。

但是流程還是一樣的,用戶登錄、獲取token、攜帶token、校驗(yàn)token。。。。。。

登錄校驗(yàn)過濾器分析

分析完流程后,就需要看看,如果不用jwt,框架自身是如何實(shí)現(xiàn)的。

找到UsernamePasswordAuthenticationFilter這個(gè)類

可以發(fā)現(xiàn)這個(gè)類主要是用來檢驗(yàn)密碼生成,憑證的。那我們只要繼承這個(gè)UsernamePasswordAuthenticationFilter類,重寫attemptAuthentication,就可以實(shí)現(xiàn)登錄校驗(yàn)功能。

所以我們的代碼是這樣的:

權(quán)限認(rèn)證過濾器分析

需要看一下BasicAuthenticationFilter的源碼

其中的關(guān)鍵方法:

思考一下,我們?nèi)绻枰脩粜畔?,?huì)從哪里???

自然是請(qǐng)求頭,前端訪問的時(shí)候,會(huì)把token放在請(qǐng)求頭。

取得token后,就要開始解析token了。token正確,將認(rèn)證信息以及權(quán)限信息注入,token不正確則可以拋出異常。這就是我們第二個(gè)過濾器的由來

一個(gè)地方需要注意一下:

這邊的userInfo,就是對(duì)應(yīng)的principal

SecurityContextHolder.getContext().getAuthentication().getPrincipal();

比如這么用:

@Component
public class SecurityUtils {
    /**
     * 獲取當(dāng)前用戶信息
     * @return 用戶信息
     */
    public UserInfo getCurrentUser() {
        return (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}

這里的UserInfo對(duì)象,就是principal。

如果你上面放的不對(duì)象,而是用戶名或者id,那么獲取principal的時(shí)候,自然是對(duì)應(yīng)的用戶名或者id了。

種瓜得瓜,種豆得豆。

配置文件

截取核心片段加點(diǎn)注釋。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 使用SpringBoot配置多數(shù)據(jù)源的經(jīng)驗(yàn)分享

    使用SpringBoot配置多數(shù)據(jù)源的經(jīng)驗(yàn)分享

    這篇文章主要介紹了使用SpringBoot配置多數(shù)據(jù)源的經(jīng)驗(yàn)分享,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • SpringBoot如何動(dòng)態(tài)改變?nèi)罩炯?jí)別

    SpringBoot如何動(dòng)態(tài)改變?nèi)罩炯?jí)別

    這篇文章主要介紹了SpringBoot如何動(dòng)態(tài)改變?nèi)罩炯?jí)別,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-12-12
  • MyBatis深入解讀懶加載的實(shí)現(xiàn)

    MyBatis深入解讀懶加載的實(shí)現(xiàn)

    顧名思義,懶加載就是因?yàn)橥祽辛?,懶得加載了,只有使用的時(shí)候才進(jìn)行加載。其實(shí),懶加載也加延遲加載,主要以應(yīng)用與Mybatis的關(guān)聯(lián)查詢,按照設(shè)置的延遲規(guī)則,推遲對(duì)延遲對(duì)關(guān)聯(lián)對(duì)象的select查詢
    2022-04-04
  • MyBatis中#{}占位符與${}拼接符的用法說明

    MyBatis中#{}占位符與${}拼接符的用法說明

    這篇文章主要介紹了MyBatis中#{}占位符與${}拼接符的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • springboot如何接收復(fù)雜參數(shù)(同時(shí)接收J(rèn)SON與文件)

    springboot如何接收復(fù)雜參數(shù)(同時(shí)接收J(rèn)SON與文件)

    文章介紹了在Spring Boot中同時(shí)處理JSON和文件上傳時(shí)使用`@RequestPart`注解的方法,`@RequestPart`可以接收多種格式的參數(shù),包括JSON和文件,并且可以作為`multipart/form-data`格式中的key
    2025-02-02
  • Struts2開發(fā) 基本配置與類型轉(zhuǎn)換

    Struts2開發(fā) 基本配置與類型轉(zhuǎn)換

    本篇文章,小編將為大家介紹關(guān)于Struts2開發(fā) 基本配置與類型轉(zhuǎn)換,有需要的朋友可以參考一下
    2013-04-04
  • Java spring的三種注入方式詳解流程

    Java spring的三種注入方式詳解流程

    Spring框架由Rod Johnson開發(fā),2004年發(fā)布了Spring框架的第一版。Spring是一個(gè)從實(shí)際開發(fā)中抽取出來的框架,因此它完成了大量開發(fā)中的通用步驟,留給開發(fā)者的僅僅是與特定應(yīng)用相關(guān)的部分,從而大大提高了企業(yè)應(yīng)用的開發(fā)效率
    2021-10-10
  • spring?batch線上異常定位記錄

    spring?batch線上異常定位記錄

    這篇文章主要為大家介紹了spring?batch線上異常定位記錄及異常解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-03-03
  • javax NotBlank和Email注解失效的解決

    javax NotBlank和Email注解失效的解決

    這篇文章主要介紹了javax NotBlank和Email注解失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • MyBatis-Plus 修改和添加自動(dòng)填充時(shí)間方式

    MyBatis-Plus 修改和添加自動(dòng)填充時(shí)間方式

    這篇文章主要介紹了MyBatis-Plus 修改和添加自動(dòng)填充時(shí)間方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評(píng)論

东方市| 卢氏县| 呼和浩特市| 锡林郭勒盟| 西充县| 虎林市| 桐柏县| 绩溪县| 来宾市| 宜丰县| 襄城县| 昌吉市| 乌拉特中旗| 英超| 冀州市| 莫力| 蕲春县| 泰兴市| 金塔县| 高安市| 金塔县| 衡水市| 增城市| 杭锦旗| 安阳市| 那曲县| 翁牛特旗| 东源县| 吉林省| 察雅县| 天镇县| 郸城县| 竹山县| 连城县| 陆河县| 来宾市| 安陆市| 新建县| 绥中县| 栾城县| 察隅县|