SpringBoot集成JWT实现token验证

SpringBoot集成JWT实现token验证

1、技术概述,描述这个技术是做什么?学习该技术的原因,技术的难点在哪里。
  • 什么是JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

  • 学习原因

开发后端登录接口,为了安全需要,防止不登录就能访问接口。

  • 技术难点

JWT请求流程

  1. 用户使用账号和密码发出post请求;
  2. 服务器使用私钥创建一个jwt;
  3. 服务器返回这个jwt给浏览器;
  4. 浏览器将该jwt串在请求头中像服务器发送请求;
  5. 服务器验证该jwt
  6. 返回响应的资源给浏览器。
2、技术详述,描述你是如何实现和使用该技术的,要求配合代码和流程图详细描述。可以再细分多个点,分开描述各个部分。
  1. 在pom.xml引入JWT依赖

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.4.0</version>
    </dependency>
    
  2. 新建annotation文件夹,自定义两个注解

    用来跳过验证的PassToken

    /**
     * PassToken
     * 跳过token验证的注解
     *
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PassToken {
        boolean required() default true;
    }
    

    需要登录才能进行操作的注解UserLoginToken(后续有需要管理员登录adminLoginToken也是同理)

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface UserLoginToken {
        boolean required() default true;
    }
    
  3. 新建TokenService类生成token

    public class TokenService {
        
        public String getToken(User user) {
            Date expiresDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            return JWT.create().withAudience(String.valueOf(user.getUserid()))
                    .sign(Algorithm.HMAC256(user.getPassword()));
        }
    }
    
  4. 新建interceptor文件,在文件中新建AuthenticationInterceptor拦截器类(userService.findUserById(userId);//这个得自己写)

    public class AuthenticationInterceptor implements HandlerInterceptor {
        @Autowired
        UserService userService;
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
            String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
            // 如果不是映射到方法直接通过
            if(!(object instanceof HandlerMethod)){
                return true;
            }
            HandlerMethod handlerMethod=(HandlerMethod)object;
            Method method=handlerMethod.getMethod();
            //检查是否有passtoken注释,有则跳过认证
            if (method.isAnnotationPresent(PassToken.class)) {
                PassToken passToken = method.getAnnotation(PassToken.class);
                if (passToken.required()) {
                    return true;
                }
            }
            //检查有没有需要用户权限的注解
            if (method.isAnnotationPresent(UserLoginToken.class)) {
                UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
                if (userLoginToken.required()) {
                    // 执行认证
                    if (token == null) {
                        throw new RuntimeException("无token,请重新登录");
                    }
                    // 获取 token 中的 user id
                    String userId;
                    try {
                        userId = JWT.decode(token).getAudience().get(0);
                    } catch (JWTDecodeException j) {
                        throw new RuntimeException("401");
                    }
                    User user = userService.findUserById(userId);//这个得自己写
                    if (user == null) {
                        throw new RuntimeException("用户不存在,请重新登录");
                    }
                    // 验证 token
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                    try {
                        jwtVerifier.verify(token);
                    } catch (JWTVerificationException e) {
                        throw new RuntimeException("token无效");
                    }
                    return true;
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, 
                                      HttpServletResponse httpServletResponse, 
                                Object o, ModelAndView modelAndView) throws Exception {
    
        }
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, 
                                              HttpServletResponse httpServletResponse, 
                                              Object o, Exception e) throws Exception {
        }
    
  5. 配置拦截器,在config文件夹中配置

    @Configuration
    public class InterceptorConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
        }
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    }
    
  6. 在util文件中新建TokenUtil接口

    public interface TokenUtil {
        static String getUserIdByRequest(HttpServletRequest httpServletRequest) {
            String token = httpServletRequest.getHeader("token");
            return String.valueOf(JWT.decode(token).getAudience().get(0));
        }
    }
    

    写接口时可通过参数获取userid

    String userid = TokenUtil.getAdminIdByRequest(httpServletRequest);
    
  7. 写接口是需要通过token验证在前加@UserLoginToken

        @UserLoginToken
        public AjaxResponse getNameByToken(HttpServletRequest httpServletRequest) {
            String userid = TokenUtil.getAdminIdByRequest(httpServletRequest);
            ...
        }
    

​ 登录接口的话就加@PassToken跳过验证

3、技术使用中遇到的问题和解决过程。

设置过期时间问题

public class TokenService {
    //设置过期时间
    private static final long EXPIRE_TIME = 7 * 24 * 3600 * 1000;//7天

    public String getToken(User user) {
        Date expiresDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        return JWT.create().withAudience(String.valueOf(user.getUserid()))
                .withExpiresAt(expiresDate)
                .sign(Algorithm.HMAC256(user.getPassword()));
    }
}
4、进行总结。

总体来说不是很难,也没叫你去理解JWT解码,就是要配置很多,但是明白流程的话,看代码会更为简单易懂。

5、列出参考文献、参考博客(标题、作者、链接)。

作者:意识流丶
链接:https://www.jianshu.com/p/e88d3f8151db
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted @ 2020-06-15 15:40  wxy402  阅读(96)  评论(0编辑  收藏  举报