JWT的Token实现自动续期
JWT认证
- 用户在浏览器中输入用户名和密码,服务器通过密码校验后生成一个 token 并保存到数据库。
- 前端获取到 token,存储到 cookie 或者 local storage 中,在后续的请求中都将带有这个 token 信息进行访问。
- 服务器获取 token 值,通过查找数据库判断当前 token 是否有效。
- 服务器也可以不存储 token,只校验颁发的 token 是否合法,合法即通过用户请求。而且从 token 中也可以解密出用户ID。
- JWT 保存在客户端,在分布式环境下不需要做额外工作。容易实现横向扩展。
- JWT 的 payload 使用的是 base64 编码的,因此在 JWT 中不能存储敏感数据。如果在 JWT 中存储了敏感信息,可以解码出来非常的不安全。
- 经过编码之后 JWT 将非常长,cookie 的限制大小一般是 4k,cookie 很可能放不下,所以 JWT 一般放在 local storage 里面。并且用户在系统中的每一次 http 请求都会把 JWT 携带在 Header 里面,HTTP 请求的 Header 可能比 Body 还要大。因此使用 JWT 的 HTTP 请求比使用 session 的开销大得多。
JWT一次性
无状态是 JWT 的特点,但也导致了这个问题,JWT 是一次性的。想修改里面的内容,就必须签发一个新的 JWT
- 无法废弃 一旦签发一个 JWT,在到期之前就会始终有效,无法中途废弃。若想废弃,一种常用的处理手段是结合 redis。
- 续签 如果使用 JWT 做会话管理,传统的 cookie 续签方案一般都是框架自带的,session 有效期 30 分钟,30 分钟内如果有访问,有效期被刷新至 30 分钟。一样的道理,要改变 JWT 的有效时间,就要签发新的 JWT。最简单的一种方式是每次请求刷新 JWT,即每个 HTTP 请求都返回一个新的 JWT。这个方法不仅暴力不优雅,而且每次请求都要做 JWT 的加密解密,会带来性能问题。另一种方法是在 redis 中单独为每个 JWT 设置过期时间,每次访问时刷新 JWT 的过期时间
功能实现
JWT依赖
1 | <dependency> |
JWT 工具类
1 | public class JWTUtil { |
说明:
- 生成的 token 中不带有过期时间,token 的过期时间由 redis 进行管理
- UserTokenDTO 中不带有敏感信息,如 password 字段不会出现在 token 中
Redis工具类
1 | public final class RedisServiceImpl implements RedisService { |
登录
1 | public String login(LoginUserVO loginUserVO) { |
说明:
- 判断用户名密码是否正确
- 用户名密码正确则生成 token
- 将生成的 token 保存至 redis
登出
1 | public boolean loginOut(String id) { |
将对应的 key 删除即可。
更新密码
1 | public String updatePassword(UpdatePasswordUserVO updatePasswordUserVO) { |
说明:更新用户密码时需要重新生成新的 token,并将新的 token 返回给前端,由前端更新保存在 local storage 中的 token,同时更新存储在 redis 中的 token,这样实现可以避免用户重新登陆,用户体验感不至于太差。
拦截器类
1 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, |
说明:拦截器中主要做两件事,一是对 token 进行校验,二是判断 token 是否需要进行续期 token 校验:
- 判断 id 对应的 token 是否不存在,不存在则 token 过期
- 若 token 存在则比较 token 是否一致,保证同一时间只有一个用户操作
token 自动续期: 为了不频繁操作 redis,只有当离过期时间只有 30 分钟时才更新过期时间
拦截器配置类
1 |
|