乐趣区

关于jwt:关于JWT-Token-自动续期的解决方案

前言

在前后端拆散的开发模式下,前端用户登录胜利后后端服务会给用户颁发一个 jwt token。前端 (如 vue) 在接管到 jwt token 后会将 token 存储到 LocalStorage 中。

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

因为 jwt token 中个别会蕴含用户的根底信息,为了保障 token 的安全性,个别会将 token 的过期工夫设置的比拟短。

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

如果真产生了这种状况前端用户必定是要骂人的,用户体验十分不敌对。本篇内容就是在前端用户无感知的状况下实现 token 的主动续期,防止频繁登录、表单填写内容失落状况的产生。

实现原理

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

  1. 登录胜利后将用户生成的 jwt token 作为 key、value 存储到 cache 缓存外面 (这时候 key、value 值一样),将缓存有效期设置为 token 无效工夫的 2 倍。
  2. 当该用户再次申请时,通过后端的一个 jwt Filter 校验 前端 token是否是无效 token,如果 token 有效表明是非法申请,间接抛出异样即可;
  3. 依据规定取出 cache token,判断 cache token 是否存在,此时次要分以下几种状况:

    • cache token 不存在
      这种状况表明该用户账户闲暇超时,返回用户信息已生效,请从新登录。
    • cache token 存在,则须要 应用 jwt 工具类验证该 cache token 是否过期超时 ,不过期无需解决。
      过期则示意该用户始终在操作只是 token 生效了,后端程序会给 token 对应的 key 映射的 value 值从新生成 jwt token 并笼罩 value 值,该缓存生命周期从新计算。

实现逻辑的外围原理:
前端申请 Header 中设置的 token 放弃不变,校验有效性以缓存中的 token 为准。

代码实现(伪码)

  1. 登录胜利后给用户签发 token,并设置 token 的有效期
...
SysUser sysUser = userService.getUser(username,password);
if(null !== sysUser){String token = JwtUtil.sign(sysUser.getUsername(), 
sysUser.getPassword());
}
...


public static String sign(String username, String secret) {
    // 设置 token 有效期为 30 分钟
    Date date = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
    // 应用 HS256 生成 token, 密钥则是用户的明码
    Algorithm algorithm = Algorithm.HMAC256(secret);
    // 附带 username 信息
    return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
}
  1. 将 token 存入 redis,并设定过期工夫,将 redis 的过期工夫设置成 token 过期工夫的两倍
Sting tokenKey = "sys:user:token" + token;
redisUtil.set(tokenKey, token);
redisUtil.expire(tokenKey, 30 * 60 * 2);
  1. 过滤器校验 token,校验 token 有效性
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    // 从 header 中获取 token
    String token = httpServletRequest.getHeader("token")
    if(null == token){throw new RuntimeException("illegal request,token is necessary!")
    }
    // 解析 token 获取用户名
    String username = JwtUtil.getUsername(token);
    // 依据用户名获取用户实体,在理论开发中从 redis 取
    User user = userService.findByUser(username);
    if(null == user){throw new RuntimeException("illegal request,token is Invalid!")
    }
    // 校验 token 是否生效,主动续期
    if(!refreshToken(token,username,user.getPassword())){throw new RuntimeException("illegal request,token is expired!")
    }
    ...
}
  1. 实现 token 的主动续期
public boolean refreshToken(String token, String userName, String passWord) {
    Sting tokenKey = "sys:user:token" + token ;
    String cacheToken = String.valueOf(redisUtil.get(tokenKey));
    if (StringUtils.isNotEmpty(cacheToken)) {
        // 校验 token 有效性,留神须要校验的是缓存中的 token
        if (!JwtUtil.verify(cacheToken, userName, passWord)) {String newToken = JwtUtil.sign(userName, passWord);
            // 设置超时工夫
            redisUtil.set(tokenKey, newToken) ;
            redisUtil.expire(tokenKey, 30 * 60 * 2);
        }
        return true;
    }
    return false;
}
...

public static boolean verify(String token, String username, String secret) {
    try {
        // 依据明码生成 JWT 效验器
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
        // 效验 TOKEN
        DecodedJWT jwt = verifier.verify(token);
        return true;
    } catch (Exception exception) {return false;}
}

本文中 jwt 的相干操作是基于 com.auth0.java-jwt 实现,大家能够通过浏览原文获取 JWTUtil 工具类。

小结

jwt token 实现逻辑的外围原理是 前端申请 Header 中设置的 token 放弃不变,校验有效性以缓存中的 token 为准,千万不要间接校验 Header 中的 token。实现原理局部大家好好领会一下,思路比实现更重要!


这里为大家筹备了一份小小的礼物,关注公众号,输出如下代码,即可取得百度网盘地址,无套路支付!

001:《程序员必读书籍》
002:《从无到有搭建中小型互联网公司后盾服务架构与运维架构》
003:《互联网企业高并发解决方案》
004:《互联网架构教学视频》
006:《SpringBoot 实现点餐零碎》
007:《SpringSecurity 实战视频》
008:《Hadoop 实战教学视频》
009:《腾讯 2019Techo 开发者大会 PPT》
010:微信交换群

退出移动版