1. 程式人生 > 程式設計 >Spring Security 整合JWT(四)

Spring Security 整合JWT(四)

一、前言

本篇文章將講述Spring Security 簡單整合JWT 處理認證授權

基本環境

  1. spring-boot 2.1.8
  2. mybatis-plus 2.2.0
  3. mysql 資料庫
  4. maven專案
Spring Security入門學習可參考之前文章:

  1. SpringBoot整合Spring Security入門體驗(一)
    blog.csdn.net/qq_38225558…
  2. Spring Security 自定義登入認證(二)
    blog.csdn.net/qq_38225558…
  3. Spring Security 動態url許可權控制(三)
    blog.csdn.net/qq_38225558…

二、 Spring Security 簡單整合 JWT

有關JWT不瞭解的可以看下官網檔案:jwt.io/introductio…在這裡插入圖片描述

1、引入jwt依賴

 <!-- jwt依賴: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.1</version>
 </dependency>複製程式碼

2、在Security登入認證成功後生成jwt令牌返回給前端儲存

jwt生成令牌程式碼如下:

// 生成jwt訪問令牌
String jwtToken = Jwts.builder()
        // 使用者角色
        .claim("ROLE_LOGIN","ADMIN")
        // 主題 - 存使用者名稱
        .setSubject("張三")
        // 過期時間 - 30分鐘
        .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
        // 加密演演算法和金鑰
        .signWith(SignatureAlgorithm.HS512,"helloworld")
        .compact();複製程式碼

這裡貼出小編文末案例demo原始碼中關於登入認證處理中的使用

@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Autowired
    private UserMapper userMapper;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 獲取前端表單中輸入後返回的使用者名稱、密碼
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();

        SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);

        boolean isValid = PasswordUtils.isValidPassword(password,userInfo.getPassword(),userInfo.getCurrentUserInfo().getSalt());
        // 驗證密碼
        if (!isValid) {
            throw new BadCredentialsException("密碼錯誤!");
        }

        // 前後端分離情況下 處理邏輯...
        // 更新登入令牌
        // 當前使用者所擁有角色程式碼
        String roleCodes = userInfo.getRoleCodes();
        // 生成jwt訪問令牌
        String jwt = Jwts.builder()
                // 使用者角色
                .claim(Constants.ROLE_LOGIN,roleCodes)
                // 主題 - 存使用者名稱
                .setSubject(authentication.getName())
                // 過期時間 - 30分鐘
                .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
                // 加密演演算法和金鑰
                .signWith(SignatureAlgorithm.HS512,Constants.SALT)
                .compact();


        User user = userMapper.selectById(userInfo.getCurrentUserInfo().getId());
        user.setToken(jwt);
        userMapper.updateById(user);
        userInfo.getCurrentUserInfo().setToken(jwt);
        return new UsernamePasswordAuthenticationToken(userInfo,password,userInfo.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}複製程式碼

前端頁面儲存的jwt令牌格式如下:在這裡插入圖片描述

3、Security訪問鑑權中認證使用者資訊

我們在訪問每一個url請求的時候,在統一認證的地方獲取jwt中我們需要的資訊然後認證即可,【注: Claims 中存放著我們需要的資訊】例如: 我們可以將使用者名稱、密碼存放jwt中,然後在認證的時候讀取到其中的使用者資訊,然後查詢資料庫認證使用者,如果滿足條件即成功訪問,如果不滿足條件即丟擲異常處理

溫馨小提示:如果jwt令牌過期,會丟擲ExpiredJwtException異常,我們需要攔截到,然後交給認證失敗處理器中處理,然後返回給前端,這裡根據個人業務實際處理即可~

// 獲取jwt中的資訊
Claims claims = Jwts.parser().setSigningKey("helloworld").parseClaimsJws(jwtToken.replace("Bearer","")).getBody();
// 獲取當前登入使用者名稱
System.out.println("獲取當前登入使用者名稱: " + claims.getSubject());複製程式碼

小編專案中認證過濾器中的使用如下:

@Slf4j
@Component
public class MyAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    AdminAuthenticationEntryPoint authenticationEntryPoint;

    private final UserDetailsServiceImpl userDetailsService;

    protected MyAuthenticationFilter(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException,IOException {
        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(response);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            // 前後端分離情況下,前端登入後將token儲存在cookie中,每次訪問介面時通過token去拿使用者許可權
            String jwtToken = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
            log.debug("後臺檢查令牌:{}",jwtToken);
            if (StringUtils.isNotBlank(jwtToken)) {
                // JWT相關start ===========================================
                // 獲取jwt中的資訊
                Claims claims = Jwts.parser().setSigningKey(Constants.SALT).parseClaimsJws(jwtToken.replace("Bearer","")).getBody();
                // 獲取當前登入使用者名稱
                System.out.println("獲取當前登入使用者名稱: " + claims.getSubject());
                // TODO 如需使用jwt特性在此做處理~
                // JWT相關end ===========================================

                // 檢查token
                SecurityUser securityUser = userDetailsService.getUserByToken(jwtToken);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    throw new BadCredentialsException("TOKEN已過期,請重新登入!");
                }
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser,null,securityUser.getAuthorities());
                // 全域性注入角色許可權資訊和登入使用者基本資訊
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(wrappedRequest,wrappedResponse);
        } catch (ExpiredJwtException e) {
            // jwt令牌過期
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest,response,null);
        } catch (AuthenticationException e) {
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest,e);
        } finally {
            stopWatch.stop();
        }
    }

}複製程式碼

簡單的入門使用就是這樣了

三、總結

  1. 引入jwt依賴
  2. 登入系統成功後生成jwt令牌返回給前端儲存到瀏覽器請求頭
  3. 在每一次請求訪問系統url時,在統一認證過濾器中獲取到請求頭中jwt令牌中儲存的使用者資訊然後做認證處理,如果滿足條件成功訪問,如果不滿足交給認證失敗處理器返回指定內容給前端

本文案例demo原始碼

gitee.com/zhengqingya…