前言

在前后端拆散的开发模式下,前端用户登录胜利后后端服务会给用户颁发一个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: 微信交换群