如何实现实现简单的JWT令牌校验

1.我们需要导入相关的maven依赖

此处我们选用jjwt的库

jjwt的优点是小巧轻量

但缺点是对JWT的一些细节包装不够, 比如 Claims只提供获取header,body

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2.封装一个JwtUtil工具类

该类中有两个方法分别为createJWT(用于创建jwt)和parseJWT(用于Token解密)

createJWT方法:

JWT令牌由三部分组成形成一个Token,分别是

  • Header:用于记录令牌类型 签名算法等

  • Payload:用于携带一些自定义信息、默认信息等(可以理解为下方的claims)

  • Signature:防止Token被篡改、确保安全性

而在方法中我们则需要完成对这三部分的设置

parseJWT方法:

我们需要在该方法中将token进行解密取出其中的claims

public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
​
        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);
​
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 设置claims
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);
        
        return builder.compact();
    }
​
    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }
​
}

3.编写JwtProperties属性类或常量类

为了代码的规范性 同时便于后续对代码的复用和维护 可以编写一个JWT相关属性类或常量类

此处我使用了@ConfigurationProperties注解 将属性写入yml配置文件中

@Component
@ConfigurationProperties(prefix = "study.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String SecretKey;
    private long Ttl;
    private String TokenName;

}
study:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    secret-key: rookiezhang
    # 设置jwt过期时间
    ttl: 7200000
    # 设置前端传递过来的令牌名称
    token-name: token

4.声明一个token拦截器类

该拦截器主要是对一些动态方法进行拦截并进行JWT校验 如Controller方法

/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getSecretKey(), token);
            Long userId = Long.valueOf(claims.get(userId).toString());
            log.info("当前用户id:", userId);
            // 将员工Id存入当前线程
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

5.在WebMVC配置类中开启自定义拦截器

使用自定义拦截器拦截为登录用户对非登录注册类页面的访问

@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
​
    @Autowired
    private JwtTokenInterceptor jwtTokenInterceptor;
​
    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenInterceptor)
                // 设置拦截路径
                .addPathPatterns("/admin/**")
                // 设置排除路径 如登录页面允许访问
                .excludePathPatterns("/admin/login");
    }
}

6.在用户登录时将token传向前端

此处提供了一个用户登录中使用Jwt工具类中的createJWT方法并将相关信息传向前端的场景

 /**
     * 登录
     *
     * @param UserLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation(value = "用户登录")
    public Result<UserLoginVO> login(@RequestBody UserLoginDTO UserLoginDTO) {
        log.info("员工登录:{}", UserLoginDTO);
​
        User user = UserService.login(UserLoginDTO);
​
        //登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(userId, user.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getSecretKey(),
                jwtProperties.getTtl(),
                claims);
​
        UserLoginVO userLoginVO = UserLoginVO.builder()
                .id(user.getId())
                .userName(user.getUsername())
                .name(user.getName())
                .token(token)
                .build();
​
        return Result.success(userLoginVO);
    }

相关推荐

  1. JWT+Redis 实现接口 Token 校验

    2024-04-10 10:32:01       38 阅读
  2. 如何实现JWT Token自动续期

    2024-04-10 10:32:01       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-10 10:32:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-10 10:32:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-10 10:32:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-10 10:32:01       18 阅读

热门阅读

  1. React Router 中常用的方法总结

    2024-04-10 10:32:01       13 阅读
  2. OceanBase 中一个关于 NOT IN 子查询的 SQL 优化案例

    2024-04-10 10:32:01       13 阅读
  3. pandas习题 028:用命名元组 namedtuple 构造 DataFrame

    2024-04-10 10:32:01       10 阅读
  4. .bat 脚本

    2024-04-10 10:32:01       10 阅读
  5. C#WPF仿苹果的漂亮的工具栏

    2024-04-10 10:32:01       12 阅读
  6. python-pytorch NLP中处理中文的步骤0.5.002

    2024-04-10 10:32:01       10 阅读