接口不掉線,用戶不登出!SpringBoot 無感刷新 Token 全解析
在現代 Web 系統中,用戶體驗與安全性的平衡是后端開發的核心命題。本文將基于實際業務場景,全面剖析如何借助 Spring Boot 實現“用戶在線不中斷,身份自動續簽”的無感刷新 Token 機制,并結合前后端聯動,構建完整的 Token 生命周期管理方案。
背景問題:為什么需要無感刷新?
想象這樣一個場景:
“我正在后臺管理系統中錄入數據,頁面突然跳轉回登錄界面,之前填寫的內容全沒了!”
這是典型的 Token 到期導致會話失效 的問題,尤其在使用 Redis 等緩存中間件存儲 Token 時尤為常見。
問題根源
后端通常通過 JWT 來實現無狀態身份驗證,但 JWT 的缺陷也很明顯:過期即失效,無法修改或撤銷。如果不設計 Token 刷新機制,用戶體驗將大打折扣。
核心策略:Token 無感續簽方案概述
方案一:后端自動續期(推薦)
在每次用戶請求時,后端檢查當前 Token 的有效時間:
- 若臨近過期(如小于5分鐘),則動態生成一個新 Token,加入響應頭中返回;
- 前端攔截響應頭,若發現新的 Token,與本地不一致則自動更新本地 Token。
方案二:前端主動續簽(補充方案)
- 前端維護一對 Token:
access_token
(短期)+refresh_token
(長期); - 每隔一段時間,前端使用
refresh_token
去調用刷新接口,獲取新的access_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/icoderoad/auth/utils/JwtUtil.java
package com.icoderoad.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/icoderoad/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 | 不暴露給前端,一般保存在 Cookie 或 HttpOnly |
標準雙 Token 模式提升了安全性和用戶體驗,避免因 AccessToken 頻繁刷新帶來的資源浪費。
特別討論:表單靜默超時的處理策略
場景問題:
用戶長時間填寫表單,沒有發出任何請求,點擊提交時發現 token 已失效,被重定向到登錄頁,數據全丟。
推薦方案:
- 提交失敗后前端本地緩存表單數據;
- 登錄成功后回顯草稿,確保用戶體驗不受損;
- 或者在用戶輸入行為時定期心跳請求,觸發后端續簽。
總結
實現無感刷新 Token,是用戶體驗與安全性協同優化的重要實踐。通過后端智能判斷與前端攔截配合,結合雙 Token 模式或動態續簽機制,我們可以實現:
用戶操作不中斷 身份憑證自動續期 安全控制粒度更靈活