SpringBoot無感刷新Token的實現(xiàn)示例
背景問題:為什么需要無感刷新?
想象這樣一個場景:
“我正在后臺管理系統(tǒng)中錄入數(shù)據(jù),頁面突然跳轉(zhuǎn)回登錄界面,之前填寫的內(nèi)容全沒了!”
這是典型的 Token 到期導(dǎo)致會話失效 的問題,尤其在使用 Redis 等緩存中間件存儲 Token 時尤為常見。
問題根源
后端通常通過 JWT 來實現(xiàn)無狀態(tài)身份驗證,但 JWT 的缺陷也很明顯:過期即失效,無法修改或撤銷。如果不設(shè)計 Token 刷新機制,用戶體驗將大打折扣。
核心策略:Token 無感續(xù)簽方案概述
方案一:后端自動續(xù)期(推薦)
在每次用戶請求時,后端檢查當前 Token 的有效時間:
- 若臨近過期(如小于5分鐘) ,則動態(tài)生成一個新 Token,加入響應(yīng)頭中返回;
- 前端攔截響應(yīng)頭,若發(fā)現(xiàn)新的 Token,與本地不一致則自動更新本地 Token。
方案二:前端主動續(xù)簽(補充方案)
- 前端維護一對 Token:
access_token(短期)+refresh_token(長期); - 每隔一段時間,前端使用
refresh_token去調(diào)用刷新接口,獲取新的access_token。
后端實現(xiàn)細節(jié)
依賴配置(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 攔截與續(xù)簽邏輯
攔截器路徑:/src/main/java/com/demo/auth/interceptor/AuthInterceptor.java
public class AuthInterceptor implements HandlerInterceptor {
private static final long REFRESH_THRESHOLD = 1000L * 60 * 5; // 剩余5分鐘內(nèi)刷新
@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) {
// 可以保存草稿后跳轉(zhuǎn)登錄
}
return Promise.reject(error);
});
關(guān)于 AccessToken 和 RefreshToken 的機制說明
| 類型 | 用途 | 特點 |
|---|---|---|
| AccessToken | 攜帶用戶身份,頻繁使用 | 安全風(fēng)險高,需短時過期 |
| RefreshToken | 用于續(xù)簽 AccessToken | 不暴露給前端,一般保存在 Cookie 或 HttpOnly |
標準雙 Token 模式提升了安全性和用戶體驗,避免因 AccessToken 頻繁刷新帶來的資源浪費。
特別討論:表單靜默超時的處理策略
場景問題:
用戶長時間填寫表單,沒有發(fā)出任何請求,點擊提交時發(fā)現(xiàn) token 已失效,被重定向到登錄頁,數(shù)據(jù)全丟。
推薦方案:
- 提交失敗后前端本地緩存表單數(shù)據(jù);
- 登錄成功后回顯草稿,確保用戶體驗不受損;
- 或者在用戶輸入行為時定期心跳請求,觸發(fā)后端續(xù)簽。
總結(jié)
實現(xiàn)無感刷新 Token,是用戶體驗與安全性協(xié)同優(yōu)化的重要實踐。通過后端智能判斷與前端攔截配合,結(jié)合雙 Token 模式或動態(tài)續(xù)簽機制,我們可以實現(xiàn):
用戶操作不中斷 身份憑證自動續(xù)期 安全控制粒度更靈活
到此這篇關(guān)于SpringBoot無感刷新Token的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot無感刷新Token內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Graphics2D drawImage圖片失真的解決方案
這篇文章主要介紹了基于Graphics2D drawImage圖片失真的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Java三種獲取redis的連接及redis_String類型演示(適合新手)
這篇文章主要介紹了Java三種獲取redis的連接及redis_String類型演示(適合新手),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
mybatis?plus配置自動create_time和update_time方式
在處理數(shù)據(jù)時,注意時間類型的轉(zhuǎn)換非常重要,不同編程環(huán)境和數(shù)據(jù)庫對時間數(shù)據(jù)的處理方式各異,因此在數(shù)據(jù)遷移或日常處理中需謹慎處理時間格式,個人經(jīng)驗表明,了解常用的時間轉(zhuǎn)換函數(shù)和方法能有效避免錯誤,提高工作效率,希望這些經(jīng)驗?zāi)転榇蠹規(guī)韼椭?/div> 2024-09-09
jasypt-spring-boot-starter實現(xiàn)加解密和數(shù)據(jù)返顯方式
這篇文章主要介紹了jasypt-spring-boot-starter實現(xiàn)加解密和數(shù)據(jù)返顯方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
Spring中@PropertySource注解使用場景解析
這篇文章主要介紹了Spring中@PropertySource注解使用場景解析,@PropertySource注解就是Spring中提供的一個可以加載配置文件的注解,并且可以將配置文件中的內(nèi)容存放到Spring的環(huán)境變量中,需要的朋友可以參考下2023-11-11最新評論

