【JWT】3. Spring Boot整合JWT

1,518 阅读2分钟

添加依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

生成JWT(Token)

public static String getToken() {
    // 声明过期时间:当前时间24小时后
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.HOUR, 24);
    Date expiredTime = calendar.getTime();
    // 获取JWT
    String token = JWT.create()
        // 设置Header,不写也行,全部使用默认值
        .withHeader(new HashMap<String, Object>())
        // 设置Payload
        .withClaim("id", 12345)
        .withClaim("username", "John")
        .withExpiresAt(expiredTime)
        // 设置Signature
        .sign(Algorithm.HMAC256("token12#@!KJH"));
    return token;
}

验证JWT(Token)

public static void verifyToken(String token) {
    // 签名的算法和密钥都必须和创建时一致
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token12#@!KJH")).build();
    // 验证拿到的Token,如果验证失败,verify方法会抛出异常
    DecodedJWT decodedJWT = jwtVerifier.verify(token);
    // 如果验证成功,则可以获取Payload中的信息
    System.out.println(decodedJWT.getClaim("id").asLong());
    System.out.println(decodedJWT.getClaim("username").asString());
}

注意:verify抛出的异常:

SignatureverificationException:签名不一致异常

TokenExpiredException:令牌过期异常

AlgorithmMismatchException:算法不匹配异常

InvalidClaimException:失效的payload异常

Springboot整合JWT

  1. 封装JWTUtils:

    /**
     * JWT工具类
     */
    public class JWTUtils {
    
        /**
         * 私钥
         */
        private static final String SECRET = "1@3$.Ssd$%7^";
    
        /**
         * 创建Token
         * @param map 前台传输的用户信息
         * @return Token
         */
        public static String getToken(Map<String, String> map) {
            JWTCreator.Builder builder = JWT.create();
    
            // 声明过期时间:7天
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, 7);
            Date expireTime = calendar.getTime();
    
            // Header默认加载,不写也行
            builder.withHeader(new HashMap<String, Object>());
    
            // Payload装载数据
            for (Map.Entry<String, String> entry : map.entrySet()) {
                builder.withClaim(entry.getKey(), entry.getValue());
            }
            builder.withExpiresAt(expireTime);
    
            // Signature配置,同时获取最终生成的Token
            String token = builder.sign(Algorithm.HMAC256(SECRET));
    
            return token;
        }
    
        /**
         * 验证Token合法性,假如Token被修改,则抛出异常
         * @param token 待验证的Token
         */
        public static void verifyToken(String token) {
            // 验证Token必须使用和创建一样的算法和私钥
            JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        }
    
         /**
          * 通过载荷名字获取载荷的值
          * @param token 已验证的Token
          * @param name payload中的key
          * @return payload中的value
          */
         public static Claim getClaimByName(String token, String name){
             return JWT.decode(token).getClaim(name);
         }
    }
    
  2. config包中创建拦截器配置类

    /**
     * 拦截器配置类
     */
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new JWTInterceptor())
                    // 拦截路径
                    .addPathPatterns("/**")
                    // 放行路径
                    .excludePathPatterns("/user/login")
                    .excludePathPatterns("/user/verifyCode");
        }
    }
    
  3. interceptor包中配置拦截器

    /**
     * JWT拦截器,对除了登录注册以外的接口进行保护。
     * 用户登录后,Token被存储在前端,每一次请求都放在Header中带给后端,中间被拦截器拦下来做验证
     */
    public class JWTInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 获取请求中的token,官方建议将token携带在请求头中
            String token = request.getHeader("token");
            Result result;
            try {
                // 验证token
                JWTUtils.verifyToken(token);
                // 放行请求
                return true;
            } catch (SignatureVerificationException e1) {
                result = Result.error(ResultCodeEnum.SIGNATURE_VERIFICATION_ERROR);
            } catch (TokenExpiredException e2) {
                result = Result.error(ResultCodeEnum.TOKEN_EXPIRED_ERROR);
            } catch (AlgorithmMismatchException e3) {
                result = Result.error(ResultCodeEnum.ALGORITHM_MISMATCH_ERROR);
            } catch (InvalidClaimException e4) {
                result = Result.error(ResultCodeEnum.INVALID_CLAIM_ERROR);
            }
            // 将result转成json传到前端
            String resultJson = new ObjectMapper().writeValueAsString(result);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(resultJson);
            // 拦截请求
            return false;
        }
    }
    
  4. 接口示例

    // 不需要再接收token,专注于业务逻辑的处理
    @GetMapping("test")
    public Result test() {
        // 业务处理
        return Result.ok();
    }
    

项目Demo

github.com/AydenBryan/…