欢迎来到YangZhenXuan的博客

技术博客总结

技术概述

JWT是一种实现无状态服务的工具,用于代替session实现登录状态的存储,减少了服务器存储sesison的开销,也解决了跨域问题。

技术详述

实现原理如图所示

一个比较通用的做法就是将JWT的生成和解析放在一个工具类中,便于全局使用

@Component
public class JwtUtil {

    @Autowired
    UserMapper userMapper;

    // application.yml中关于jwt的配置属性
    public static final String secretKey = "SECRET_KEY";

    // token 1小时候过期
    public static final Long expire = 60 * 60 * 1000L;

    /*
    	创建Token
    */
    public String createToken(User user){
        // 根据登录的User对象信息生成Token
        JwtBuilder jwtBuilder = Jwts.builder()
                .setId(user.getId() + "")
                .setSubject(user.getUsername())
                .signWith(SignatureAlgorithm.HS256, secretKey);
        // 向token中添加键值对信息
        return jwtBuilder.compact();
    }

    /*
    	解析Token
    */
    public User parseToken(String token){
        Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody();

        // 从解析结果中获得键值对信息
        String id = claims.getId();
        System.out.println(id);
        Integer userId = Integer.parseInt(id);
        // 从数据库中查找User
        return userMapper.getUserById(userId);
    }

    /*
        验证token
    */
    public boolean validateToken(String token){
        try{
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            return true;
        }
        catch(SignatureException e){
            ExceptionThrower.cast(ResponseCode.INVALID_TOKEN);
        }
        catch(ExpiredJwtException e){
            ExceptionThrower.cast(ResponseCode.TOKEN_EXPIRED);
        }
        return false;
    }
}

之后添加一个拦截器,负责拦截所有请求,并判断请求Header中是否有Token,若有则解析出操作用户。

// 部分代码
@Slf4j
public class JwtInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    JwtUtil jwtUtil;

    public static final String ADMIN = "ADMIN";
    public static final String USER = "USER";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        String token = request.getHeader("Authorization");
        String requestURI = request.getRequestURI();
        log.info("接受到token:"+token);
        log.info("请求路径:"+ requestURI);

      
      // 鉴权判断

    }

}

之后的工作便是在登录时生成Token并返回给前端,前端发送axios请求时将token放入header中即可。

问题与解决

我们的项目中使用jwt的原因是Shiro在跨域情况下无法保持用户session一致,因此shiro能实现的,jwt也一定要实现,例如鉴权操作。
经过网上查阅相关资料,jwt的鉴权也可以通过注解来实现。首先先定义几个注解

/**
 * 不需要登录
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

/**
 * 需要具备某些角色
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequireRoles {
    String[] roles() default {};
}

之后在拦截的时候,根据要访问的控制器方法获取其注解,再根据token获取用户对象信息,根据用户权限进行拦截即可。

// 通过请求
        if (method.isAnnotationPresent(PassToken.class)){
            log.info("不需要权限,放行");
            return true;
        }
        // 需要登录
        if (method.isAnnotationPresent(RequireAuthorization.class))
        {
            log.info("需要USER权限");
            return requireUSERRole(token);
        }

总结

没啥特别难理解的东西,就是Token的刷新机制暂时没有运用到项目中,待以后慢慢研究。

posted @ 2021-06-27 14:08  YamaiKaguya  阅读(43)  评论(0编辑  收藏  举报