美文网首页
json-web-token(JWT)

json-web-token(JWT)

作者: 贪挽懒月 | 来源:发表于2020-04-08 16:28 被阅读0次

    一、JWT是什么?

    JWT的全称为json web token。不要把它想得多么高深,其实就是一种生成token的方式。一般我们访问一个系统的流程就是:请求登录接口,该接口会返回一个token,请求其他接口都要带上token,token验证通过才能访问成功,而JWT可以理解为就是生成token的一种机制。


    欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。


    二、JWT怎么用?

    1、添加jwt的依赖:

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

    2、新建一个TokenService,用来生成token:

    @Service
    public class TokenService {
        // token过期时间5分钟
        private static final long EXPIRE_TIME = (60 * 1000 * 5);
    
        public String getToken(User user) {
            return JWT.create()
                    // 需要放入token中的信息
                    .withAudience(user.getId())
                    // 设置token过期时间
                    .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRE_TIME))
                    // 用户密码当作密钥
                    .sign(Algorithm.HMAC256(user.getPassword()));
        }
    }
    

    User类就是一个普通的pojo,这里就不把代码贴出来了。这个getToken方法表示将用户的密码作为密钥,把用户的id放进token中,设置token过期时间为5分钟。

    3、新建两个注解,一个注解表示需要验证,另一个表示跳过验证:

    • 需要验证:
    //可以作用在方法,类和接口上
    @Target({ElementType.METHOD, ElementType.TYPE})
    //编译器会将SkipToken的信息保存在虚拟机中
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NeedToken {
        // required 属性默认值为true
        boolean required() default true;
    }
    
    • 跳过验证:
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SkipToken {
        boolean required() default true;
    }
    

    4、新建一个AuthInterceptor类,用于拦截请求:

    public class AuthInterceptor implements HandlerInterceptor {
        @Autowired
        private UserService userService;
    
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object object) {
            // 从 http 请求头中取出 token
            String token = httpServletRequest.getHeader("token");
            // 只拦截方法,不是方法直接返回true
            if (!(object instanceof HandlerMethod)) {
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod) object;
            Method method = handlerMethod.getMethod();
            // 有SkipToken注解的直接跳过认证
            if (method.isAnnotationPresent(SkipToken.class)) {
                SkipToken skipToken = method.getAnnotation(SkipToken.class);
                if (skipToken.required()) {
                    return true;
                }
            }
            // 有NeedToken注解的就进行认证
            if (method.isAnnotationPresent(NeedToken.class)) {
                NeedToken needToken = method.getAnnotation(NeedToken.class);
                if (needToken.required()) {
                    if (token == null) {
                        throw new RuntimeException("无token,请重新登录");
                    }
                    // 获取 token 中的 userId
                    String userId;
                    try {
                        userId = JWT.decode(token).getAudience().get(0);
                    } catch (JWTDecodeException j) {
                        throw new RuntimeException("401");
                    }
                    // 根据userId查询数据库
                    User user = userService.findUserById(userId);
                    if (user == null) {
                        throw new RuntimeException("用户不存在,请重新登录");
                    }
                    // 用查出来的user的密码去校验token
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                    try {
                        // 没发生异常就表示校验通过
                        jwtVerifier.verify(token);
                    } catch (JWTVerificationException e) {
                        throw new RuntimeException("401");
                    }
                    return true;
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Object o,ModelAndView modelAndView) {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, Exception e) {
        }
    }
    

    这个拦截器的处理逻辑就是:

    • 只拦截方法,如果不是方法,就放行;
    • 如果拦截的方法有@SkipToken注解,放行;
    • 如果拦截的方法有@NeedToken注解,则需要验证token;
    • 取出请求头中的token,拿出token中的userId,根据此userId去数据库查询对应的记录;
    • 再将查出来的user的password去验证token,验证成功则放行;
    • 如果没有@NeedToken也没有@SkipToken注解的,也放行。

    5、新建一个InterceptorConfig类,将我们上面写的拦截器配置到spring容器中去:

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authInterceptor())
                    .addPathPatterns("/**");   
        }
        @Bean
        public AuthInterceptor authInterceptor() {
            return new AuthInterceptor();
        }
    }
    

    6、在controller中的用法:

    @RestController
    @RequestMapping("api")
    public class UserController {
        @Autowired
        private UserService userService;
        @Autowired
        private TokenService tokenService;
       
        @SkipToken
        @PostMapping("/user")
        public JsonResult login(User user){
            User dbUser = userService.findForLogin(user);
            if (dbUser == null){
                return new JsonResult(200, "用户名或密码错误", null);
            } else {
                String token = tokenService.getToken(dbUser);
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("token", token);
                resultMap.put("user", dbUser);
                return new JsonResult(200, "登录成功", resultMap);
            }
        }
        
        @NeedToken
        @GetMapping("/getMessage")
        public String getMessage(){
            return "你已通过验证";
        }
        
        @SkipToken
        @GetMapping("/noToken")
        public String noToken() {
            return "无需token访问";
        }
    }
    

    以上就是JWT的用法,其实在实际生产中一般会配合shiro使用。

    相关文章

      网友评论

          本文标题:json-web-token(JWT)

          本文链接:https://www.haomeiwen.com/subject/vompjhtx.html