SpringBoot学习:接口鉴权JWT

JWT
全称Json Web Token,是为了在应用间传递声明的基于json的开放式标准,用于双方之间以json的形式传递安全信息,因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

JWT结构
jwt由三段信息构成,以.号隔开,连接在一起就构成了jwt字符串,如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJzdWIiOiIxIiwiaXNzIjoiaHMiLCJleHAiOjE1NjAzOTM3NzB9
.ooOZAXtruR_aWKbQofolC8TXaKU65I9nPubhBW3LRq4

如上.号隔开的三段信息,分别代表着头部,负载,签证

  • Header
    JWT的头部承载两部分信息:token类型和采用的加密算法

    { 
      "alg": "HS256",
       "typ": "JWT"
    } 
    
  • Payload
    iss: jwt签发者
    sub: 面向的用户(jwt所面向的用户)
    aud: 接收jwt的一方
    exp: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间)
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

  • Signature
    这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

SpringBoot集成JWT

  • JwtUtils :

    
    import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
    import static org.apache.commons.lang3.math.NumberUtils.toInt;
    import static org.apache.commons.lang3.math.NumberUtils.toLong;
    
    /**
     * @author: hs
     * @Date: 2019/6/11 20:58
     * @Description:
     */
    @Data
    @Component
    @ConfigurationProperties(prefix = "api.jwt")
    public class JwtUtils {
    
        private static final long EXPIRE_TIME = 30 * 60;
    
        private static final String SECRET = "HYBI%&J)(UJJ&TG&";
    
        private Long expire;
    
        private String secret;
    
    
        /**
         * @return 加密的token
         */
        public String sign(UserInfo userInfo) {
            Date date = new Date(System.currentTimeMillis() + toLong(this.expire.toString(), EXPIRE_TIME) * 1000);
            Algorithm algorithm = Algorithm.HMAC256(defaultIfBlank(this.secret, SECRET));
            // 附带username信息
            return JWT.create()
                    .withSubject(userInfo.getId().toString())
                    .withIssuer(userInfo.getUsername())
                    .withExpiresAt(date)
                    .sign(algorithm);
        }
    
        /**
         * 校验token是否正确
         *
         * @param token 密钥
         * @return 是否正确
         */
        public Map<String, Claim> verify(String token, String username) {
            try {
                Algorithm algorithm = Algorithm.HMAC256(defaultIfBlank(this.secret, SECRET));
                JWTVerifier verifier = JWT.require(algorithm).withIssuer(username).build();
                DecodedJWT jwt = verifier.verify(token);
                return jwt.getClaims();
            } catch (Exception e) {
                throw AppointException.errorMessage("鉴权失败,无效的用户");
            }
        }
    
        /**
         * 获得token中的信息无需secret解密也能获得
         *
         * @return token中包含的用户名
         */
        public String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getIssuer();
            } catch (JWTDecodeException e) {
                throw AppointException.errorMessage("无效的token,请重新登录");
            }
        }
    
        /**
         * 获得token中的信息无需secret解密也能获得
         *
         * @return token中包含的id
         */
        public Integer getUserId(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return toInt(jwt.getSubject());
            } catch (JWTDecodeException e) {
                throw AppointException.errorMessage("无效的token,请重新登录");
            }
        }
    }
    
  • 登录认证拦截器

    @Component
    public class AuthenticationInterceptor implements HandlerInterceptor {
    
        private static final String TOKEN = "token";
    
        private final JwtUtils jwtUtils;
    
        @Autowired
        public AuthenticationInterceptor(JwtUtils jwtUtils) {
            this.jwtUtils = jwtUtils;
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    
            // 从 http 请求头中取出 token
            String token = request.getHeader(TOKEN);
            if (StringUtils.isBlank(token)) {
                token = request.getParameter(TOKEN);
            }
    
            // 如果不是映射到方法直接通过
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
    
            //LoginSkip,有则跳过认证
            if (method.isAnnotationPresent(LoginSkip.class)) {
                LoginSkip loginSkip = method.getAnnotation(LoginSkip.class);
                if (loginSkip.required()) {
                    return true;
                }
            }
            //检查有没有需要用户权限的注解
            if (method.isAnnotationPresent(LoginAuth.class)) {
                LoginAuth userLogin = method.getAnnotation(LoginAuth.class);
                if (userLogin.required()) {
                    // 执行认证
                    if (StringUtils.isBlank(token)) {
                        throw new RuntimeException("无效token,请重新登录");
                    }
                    String username = jwtUtils.getUsername(token);
                    jwtUtils.verify(token, username);
                    return true;
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    
        }
    }
    
  • 拦截器注册配置

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
        private final AuthenticationInterceptor authenticationInterceptor;
    
        @Autowired
        public InterceptorConfig(AuthenticationInterceptor authenticationInterceptor) {
            this.authenticationInterceptor = authenticationInterceptor;
        }
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor).addPathPatterns("/**");
        }
    }
    
  • api接口

    @RestController
    @RequestMapping("/api")
    @Api(description = "登录")
    public class LoginController {
    
        private final LoginService loginService;
    
        private final JwtUtils jwtUtils;
    
        @Autowired
        public LoginController(LoginService loginService, JwtUtils jwtUtils) {
            this.loginService = loginService;
            this.jwtUtils = jwtUtils;
        }
    
    
        @PostMapping("/login")
        @ApiOperation(value = "login接口", notes = "jwt接口权限验证")
        public AbstractApiResult login(@RequestBody UserInfo userInfo) {
            UserInfo user = loginService.getUserInfo(userInfo.getUsername());
            if (user == null) {
                throw AppointException.errorMessage(100, "用户名不存在");
            }
            String token = jwtUtils.sign(user);
            return AbstractApiResult.success(token);
        }
    
        @LoginAuth
        @GetMapping("testToken")
        @ApiOperation(value = "token", notes = "token测试")
        public String getMessage(@RequestHeader String token) {
            return "success";
        }
    
    }
    
  • 测试
    访问接口

    curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \ 
       "id": 0, \ 
       "username": "hs", \ 
       "password": "string" \ 
     }' 'http://localhost:9999/api/login'
    

    SpringBoot学习:接口鉴权JWT_第1张图片
    带着token令牌eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHMiLCJleHAiOjE1NjAzOTM3NzB9.ooOZAXtruR_aWKbQofolC8TXaKU65I9nPubhBW3LRq4访问接口:

    curl -X GET --header 'Accept: text/plain' --header 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHMiLCJleHAiOjE1NjAzOTM3NzB9.ooOZAXtruR_aWKbQofolC8TXaKU65I9nPubhBW3LRq4' 'http://localhost:9999/api/testToken'
    

    在这里插入图片描述

你可能感兴趣的:(Utils,SpringBoot学习总结)