关于 JWT Token 自动续期的解决(根据其他文献参考写的)

关于 JWT Token 自动续期的解决

关于 JWT Token 自动续期的解决方案,最近在做token,因为session无法适应小程序,所以改为token,找的了一篇很好的文章,参考实现了之后想分享给大家,里面添加了部分自己的代码。文章最后是我参考的文献。

在前后端分离的开发模式下,前端用户登录成功后后端服务会给用户颁发一个 jwt token。前端(如 vue)在接收到 jwt token 后会将 token 存储到 LocalStorage 中。

后续每次请求都会将此 token 放在请求头中传递到后端服务,后端服务会有一个过滤器对 token 进行拦截校验,校验 token 是否过期,如果 token 过期则会让前端跳转到登录页面重新登录。

因为 jwt token 中一般会包含用户的基础信息,为了保证 token 的安全性,一般会将 token 的过期时间设置的比较短。

但是这样又会导致前端用户需要频繁登录(token 过期),甚至有的表单比较复杂,前端用户在填写表单时需要思考较长时间,等真正提交表单时后端校验发现 token 过期失效了不得不跳转到登录页面。

如果真发生了这种情况前端用户肯定是要骂人的,用户体验非常不友好。本篇内容就是在前端用户无感知的情况下实现 token 的自动续期,避免频繁登录、表单填写内容丢失情况的发生。

实现原理

jwt token 自动续期的实现原理如下:

登录成功后将用户生成的 jwt token 作为 key、value 存储到 cache 缓存里面 (这时候 key、value 值一样),将缓存有效期设置为 token 有效时间的 2 倍。
当该用户再次请求时,通过后端的一个 jwt Filter 校验前端 token 是否是有效 token,如果 token 无效表明是非法请求,直接抛出异常即可;
根据规则取出 cache token,判断 cache token 是否存在,此时主要分以下几种情况:
cache token 不存在
这种情况表明该用户账户空闲超时,返回用户信息已失效,请重新登录。
cache token 存在,则需要使用 jwt 工具类验证该 cache token 是否过期超时,不过期无需处理。
过期则表示该用户一直在操作只是 token 失效了,后端程序会给 token 对应的 key 映射的 value 值重新生成 jwt token 并覆盖 value 值,该缓存生命周期重新计算。
实现逻辑的核心原理:
前端请求 Header 中设置的 token 保持不变,校验有效性以缓存中的 token 为准。

代码实现(伪码)

0、pom.xml文件

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

1、登录拦截+成功后给用户签发 token,并设置 token 的有效期

    /**
     * 添加拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
        String[] excludes=new String[]{
   "/","/doc/**","/css/**","/js/**","/fonts/**","/HUI/**","/img/**","/ztree/**","/register/**"
                ,"/login/**","/files/**","/test/**"};
        registry.addInterceptor(getLoginHandlerInterceptor()).addPathPatterns("/**")
                .excludePathPatterns(excludes);

    }
        //生成签名 用于客户端验证
        String token = JwtUtil.sign(users.getContactNumber(),users.getUserPassword());
        // 设置到redis  5分钟后redis的token过期
        token = tokenUtil.setToken(TokenUtil.REDIS_EXPIRE_TIME,token);
        // 响应带token
        response.setHeader("token",token);

2、将 token 存入 redis,并设定过期时间,将 redis 的过期时间设置成 token 过期时间的两倍

		String tokenKey = "sys:user:token" + token;
        if (redisUtil.hasKey(tokenKey)){
   
            redisUtil.del(tokenKey);
        }
        boolean result = redisUtil.set(tokenKey, token,time);

3、过滤器校验 token,校验 token 有效性

 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
   
        //从header中获取token
        String token = request.getHeader("token");
        UserTable user = tokenUtil.getUserByToken(request);
        if(user == null){
   
            // token的验证方法
            rejectForJson(ErrCodeEnum.TOKEN_NOT_EXIST, response, request);
            return false;
        }
        //校验token是否失效,自动续期
        if(!tokenUtil.refreshToken(token,user)){
   
            rejectForJson(ErrCodeEnum.TOKEN_NOT_EXPIRE, response, request);
            return false;
        }
        String me= request.getMethod();
         if("OPTIONS".equals(me)){
   
             return true;
         }
        return true;
    }

4、实现 token 的自动续期

    public boolean refreshToken(String token, UserTable userTable) {
   
        // 是否是redis里的token
        String tokenKey = "sys:user:token" + token;
        String cacheToken = String.valueOf(redisUtil.get(tokenKey));
        if (StringUtils.isNotEmpty(cacheToken)) {
   
            // 校验token有效性,注意需要校验的是缓存中的token
            if (!JwtUtil.verify(cacheToken, userTable.getContactNumber(), userTable.getUserPassword())) {
   
                String newToken = JwtUtil.sign(userTable.getContactNumber(), userTable.getUserPassword());
                // 设置超时时间
                redisUtil.set(tokenKey, newToken,REDIS_EXPIRE_TIME*2);
            }
            return true;
        }
        return false;
    }

5、几个工具类

一、JwtUtil.java

package com.sinan.snwit.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class JwtUtil {
   
    //设置签名的过期时间 60s
    public static final long EXPIRE_TIME = 60*1000;

    /**
     * 校验token是否正确
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String contactNumber, String secret) {
   
        try {
   
            // 根据密码生成JWT效验器
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm).withClaim("contactNumber", contactNumber
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值