背景问题:为什么需要无感刷新?
想象这样一个场景:
“我正在后台管理系统中录入数据,页面突然跳转回登录界面,之前填写的内容全没了!”
这是典型的 Token 到期导致会话失效 的问题,尤其在使用 Redis 等缓存中间件存储 Token 时尤为常见。
问题根源
后端通常通过 JWT 来实现无状态身份验证,但 JWT 的缺陷也很明显:过期即失效,无法修改或撤销。如果不设计 Token 刷新机制,用户体验将大打折扣。
核心策略:Token 无感续签方案概述
方案一:后端自动续期(推荐)
在每次用户请求时,后端检查当前 Token 的有效时间:
- 若临近过期(如小于5分钟) ,则动态生成一个新 Token,加入响应头中返回;
- 前端拦截响应头,若发现新的 Token,与本地不一致则自动更新本地 Token。
方案二:前端主动续签(补充方案)
- 前端维护一对 Token:(短期)+ (长期);
- 每隔一段时间,前端使用 去调用刷新接口,获取新的 。
后端实现细节
依赖配置(pom.xml)
- <dependencies>
- <dependency>
- <groupId>cn.hutool</groupId>
- <artifactId>hutool-all</artifactId>
- <version>5.5.1</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.33</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt</artifactId>
- <version>0.9.1</version>
- </dependency>
- </dependencies>
复制代码
JWT 工具类JwtUtil.java
代码路径: - /src/main/java/com/demo/auth/utils/JwtUtil.java
复制代码
- package com.demo.auth.utils;
-
-
- import io.jsonwebtoken.*;
- import javax.crypto.SecretKey;
- import javax.crypto.spec.SecretKeySpec;
- import java.util.*;
-
-
- public class JwtUtil {
- public static final long JWT_TTL = 1000L * 60 * 60 * 24; // 24小时
- public static final String JWT_KEY = "qx";
-
-
- public static String createJWT(String subject) {
- return getJwtBuilder(subject, null, UUID.randomUUID().toString().replace("-", "")).compact();
- }
-
-
- public static String createJWT(String subject, Long ttlMillis) {
- return getJwtBuilder(subject, ttlMillis, UUID.randomUUID().toString()).compact();
- }
-
-
- private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
- long nowMillis = System.currentTimeMillis();
- long expMillis = (ttlMillis != null ? nowMillis + ttlMillis : nowMillis + JWT_TTL);
- SecretKey secretKey = generalKey();
- return Jwts.builder()
- .setId(uuid)
- .setSubject(subject)
- .setIssuer("icoderoad")
- .setIssuedAt(new Date(nowMillis))
- .setExpiration(new Date(expMillis))
- .signWith(SignatureAlgorithm.HS256, secretKey);
- }
-
-
- public static Claims parseJWT(String jwt) throws Exception {
- return Jwts.parser()
- .setSigningKey(generalKey())
- .parseClaimsJws(jwt)
- .getBody();
- }
-
-
- public static SecretKey generalKey() {
- byte[] key = Base64.getDecoder().decode(JWT_KEY);
- return new SecretKeySpec(key, 0, key.length, "AES");
- }
-
-
- public static Date getExpiration(String jwt) {
- try {
- return parseJWT(jwt).getExpiration();
- } catch (Exception e) {
- throw new RuntimeException("Token 解析失败", e);
- }
- }
- }
复制代码
Token 拦截与续签逻辑
拦截器路径: - /src/main/java/com/demo/auth/interceptor/AuthInterceptor.java
复制代码
- public class AuthInterceptor implements HandlerInterceptor {
-
-
- private static final long REFRESH_THRESHOLD = 1000L * 60 * 5; // 剩余5分钟内刷新
-
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-
-
- String token = request.getHeader("Authorization");
- if (StringUtils.isEmpty(token)) {
- throw new RuntimeException("未登录");
- }
-
-
- Claims claims = JwtUtil.parseJWT(token);
- long now = System.currentTimeMillis();
- long exp = claims.getExpiration().getTime();
-
-
- if (exp - now < REFRESH_THRESHOLD) {
- String newToken = JwtUtil.createJWT(claims.getSubject());
- response.setHeader("X-Token-Refresh", newToken);
- }
-
-
- return true;
- }
- }
复制代码
前端处理逻辑(以 Vue + Axios 为例)
前端拦截代码:
- axios.interceptors.response.use(response => {
- const newToken = response.headers['x-token-refresh'];
- if (newToken && newToken !== localStorage.getItem('access_token')) {
- localStorage.setItem('access_token', newToken);
- }
- return response;
- }, error => {
- // 处理401
- if (error.response.status === 401) {
- // 可以保存草稿后跳转登录
- }
- return Promise.reject(error);
- });
复制代码
关于 AccessToken 和 RefreshToken 的机制说明
| 类型 | 用途 | 特点 |
|---|
| AccessToken | 携带用户身份,频繁使用 | 安全风险高,需短时过期 | | RefreshToken | 用于续签 AccessToken | 不暴露给前端,一般保存在 Cookie 或 HttpOnly |
标准双 Token 模式提升了安全性和用户体验,避免因 AccessToken 频繁刷新带来的资源浪费。
特别讨论:表单静默超时的处理策略
场景问题:
用户长时间填写表单,没有发出任何请求,点击提交时发现 token 已失效,被重定向到登录页,数据全丢。
推荐方案:
- 提交失败后前端本地缓存表单数据;
- 登录成功后回显草稿,确保用户体验不受损;
- 或者在用户输入行为时定期心跳请求,触发后端续签。
总结
实现无感刷新 Token,是用户体验与安全性协同优化的重要实践。通过后端智能判断与前端拦截配合,结合双 Token 模式或动态续签机制,我们可以实现:
用户操作不中断 身份凭证自动续期 安全控制粒度更灵活
到此这篇关于SpringBoot无感刷新Token的实现示例的文章就介绍到这了,更多相关SpringBoot无感刷新Token内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯! 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |