欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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ù)庫讀取權(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)文章

  • Java實(shí)現(xiàn)企業(yè)發(fā)放的獎(jiǎng)金根據(jù)利潤(rùn)提成問題

    Java實(shí)現(xiàn)企業(yè)發(fā)放的獎(jiǎng)金根據(jù)利潤(rùn)提成問題

    這篇文章主要介紹了請(qǐng)利用數(shù)軸來分界,定位。注意定義時(shí)需把獎(jiǎng)金定義成長(zhǎng)整型,需要的朋友可以參考下
    2017-02-02
  • Eclipse 出現(xiàn)Failed to load JavaHL Library解決方法

    Eclipse 出現(xiàn)Failed to load JavaHL Library解決方法

    這篇文章主要介紹了Eclipse 出現(xiàn)Failed to load JavaHL Library解決方法的相關(guān)資料,今天使用Eclipse 時(shí)出現(xiàn)以上錯(cuò)誤,本文說明如何更更正,需要的朋友可以參考下
    2016-11-11
  • SpringBoot個(gè)性化配置的方法步驟

    SpringBoot個(gè)性化配置的方法步驟

    這篇文章主要介紹了SpringBoot個(gè)性化配置的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-02-02
  • Windows使用多個(gè)JDK的方法詳解

    Windows使用多個(gè)JDK的方法詳解

    本文介紹了如何在Windows系統(tǒng)中同時(shí)使用多個(gè)JDK版本(JDK8和JDK21),并詳細(xì)描述了修改環(huán)境變量和Path變量的步驟,以實(shí)現(xiàn)JDK版本的切換
    2024-12-12
  • 子線程任務(wù)發(fā)生異常時(shí)主線程事務(wù)回滾示例過程

    子線程任務(wù)發(fā)生異常時(shí)主線程事務(wù)回滾示例過程

    這篇文章主要為大家介紹了子線程任務(wù)發(fā)生了異常時(shí)主線程事務(wù)如何回滾的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • Spring Boot 中的自動(dòng)配置autoconfigure詳解

    Spring Boot 中的自動(dòng)配置autoconfigure詳解

    這篇文章主要介紹了Spring Boot 中的自動(dòng)配置autoconfigure詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-01-01
  • IDEA中配置Python環(huán)境并運(yùn)行方式

    IDEA中配置Python環(huán)境并運(yùn)行方式

    本文介紹了在Mac和Windows平臺(tái)上安裝Python環(huán)境的方法,并詳細(xì)講解了如何在IntelliJ IDEA中安裝Python插件、創(chuàng)建Python工程和運(yùn)行Python文件,同時(shí),還提到了一些常用的Python框架,如Django、Google App Engine和SQL支持
    2025-03-03
  • Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    Spring中BeanFactory與FactoryBean接口的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于Spring中BeanFactory與FactoryBean接口的區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java Spring登錄練習(xí)詳解

    Java Spring登錄練習(xí)詳解

    這篇文章主要介紹了Java編程實(shí)現(xiàn)spring簡(jiǎn)單登錄的練習(xí),具有一定參考價(jià)值,需要的朋友可以了解下,希望能夠給你帶來幫助
    2021-10-10
  • Java8新的異步編程方式CompletableFuture實(shí)現(xiàn)

    Java8新的異步編程方式CompletableFuture實(shí)現(xiàn)

    這篇文章主要介紹了Java8新的異步編程方式CompletableFuture實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-04-04

最新評(píng)論