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

标签: | 发表时间:2020-10-07 16:02 | 作者:
出处:https://mp.weixin.qq.com

前言

在前后端分离的开发模式下,前端用户登录成功后后端服务会给用户颁发一个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。实现原理部分大家好好体会一下,思路比实现更重要!

相关 [jwt token] 推荐:

什么是 JWT -- JSON WEB TOKEN - 简书

- -
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(. (RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景. JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密.

JWT认证,取得过期token的用户名

- - 掘金后端
项目前后端分离,请求认证使用的是JWT无状态认证. 最近遇到一个问题,认证token需要从token中获取用户名,但是这个token有可能是过期的. 一般取得用户名用的是下面的代码. .getBody(); } 复制代码. 如果token没有过期,则可以通过Claims.getSubject()获得用户名.

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

- -
在前后端分离的开发模式下,前端用户登录成功后后端服务会给用户颁发一个jwt token. 前端(如vue)在接收到jwt token后会将token存储到LocalStorage中. 后续每次请求都会将此token放在请求头中传递到后端服务,后端服务会有一个过滤器对token进行拦截校验,校验token是否过期,如果token过期则会让前端跳转到登录页面重新登录.

Refresh Token的使用场景以及如何与JWT交互 - 我是钟钟的个人空间 - OSCHINA

- -
OAuth2定义的Refresh Token的概念. 我们将会明白为什么他们会这样做,以及他们如何与其他类型的Token进行比较. 我们也将通过一个简单的例子来学习如何使用它们. 更新:目前这篇文章写的Auth0还没有通过. OpenID Connect认证. access token)不符合此规范,但符合.

RSA的SecureID token数据被偷了?

- ripwu - 张志强的网络日志
博客 » 记事本 » 密码学 ». WSJ报道:RSA承认其数据被偷,4000万SecureID token需要被更新. 中国银行银行密钥用的就是RSA生产,就是下图这玩意儿,手里有这玩意儿的同学们要小心了(当然,如果你的账户里的钱没有6位数以上,也不用太担心,毕竟网银的安全性不全依赖于这个设备):.

服务器session和jwt之争

- - zzm
session 和cookie的目的相同,都是为了克服http协议无状态的缺陷,但完成的方法不同. session通过cookie,在客户端保存session id,而将用户的其他会话消息保存在服务端的session对象中,与此相对的,cookie需要将所有信息都保存在客户端. 因此cookie存在着一定 的安全隐患,例如本地cookie中保存的用户名密码被破译,或cookie被其他网站收集(例如:1.

微信平台的token安全验证(转)

- - 行业应用 - ITeye博客
本文目标:学习一种比较安全的服务器间互相验证身份的方式. 问题:开发微信公众平台接口,开发者的服务器为了确保请求是否来自微信服务器,应该如何去做. 1)  在微信管理页面上填写URL和TOKEN,开发者服务器上也记录同样的TOKEN. 2)  微信服务器发送HTTP请求,附带上参数(注意TOKEN是不会被传输的).

springmvc下的基于token的防重复提交

- - zzm
现在的网站在注册步骤中,由于后台要处理大量信息,造成响应变慢(测试机器性能差也是造成变慢的一个因素),在前端页面提交信息之前,等待后端响应,此时如果用户. 再点一次提交按钮,后台会保存多份用户信息. 为解决此问题,借鉴了struts2的token思路,在springmvc下实现token. 在springmvc配置文件中加入拦截器的配置,拦截两类请求,一类是到页面的,一类是提交表单的.

[Web 安全] 如何通过JWT防御CSRF

- - SegmentFault 最新的文章
先解释两个名词,CSRF 和 JWT. CSRF (Cross Site Request Forgery),它讲的是你在一个浏览器中打开了两个标签页,其中一个页面通过窃取另一个页面的 cookie 来发送伪造的请求,因为 cookie 是随着请求自动发送到服务端的. JWT (JSON Web Token),通过某种算法将两个 JSON 对象加密成一个字符串,该字符串能代表唯一用户.

用JWT技术为SpringBoot的API增加授权保护 – springcamp

- -
示例 https://github.com/qihaiyan/jwt-boot-auth. 用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的. 现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能够访问API.