Token無感知刷新前端
思路
Token無感知刷新是一種常見的技術(shù),就是在用戶無感知的情況下自動處理Token過期的問題,避免用戶因Token過期而被迫重新登錄。以下是實現(xiàn)Token無感知刷新的主要步驟和考慮因素:
一、Token生成與存儲
- Token生成:
- 在用戶登錄成功后,后端會生成兩個Token:一個AccessToken(用于訪問受保護的API,過期時間較短)和一個RefreshToken(用于獲取新的AccessToken,過期時間較長)。
- 可以使用JWT(JSON Web Tokens)或其他安全機制來生成和驗證Token。
- Token存儲:
將AccessToken和RefreshToken存儲在客戶端的本地緩存中,如localStorage或sessionStorage。
確保RefreshToken的安全性,避免在客戶端以明文形式暴露。
二、請求攔截器設(shè)置
- 請求攔截器:
- 在發(fā)送請求之前,通過請求攔截器檢查AccessToken是否存在并未過期。
- 如果AccessToken存在且未過期,則將其添加到請求的Authorization頭部。
- 如果AccessToken不存在或已過期,則嘗試使用RefreshToken獲取新的AccessToken。
- 響應(yīng)攔截器:
在接收到響應(yīng)后,通過響應(yīng)攔截器檢查響應(yīng)狀態(tài)碼。
如果狀態(tài)碼為401(未授權(quán)),則表明AccessToken已過期,此時應(yīng)使用RefreshToken嘗試獲取新的AccessToken。
如果狀態(tài)碼為200(成功)或其他有效狀態(tài)碼,則直接處理響應(yīng)數(shù)據(jù)。
三、Token刷新邏輯
- 檢查Token是否過期:
- 可以在請求攔截器中檢查AccessToken的過期時間,但這需要后端提供Token的過期時間字段,且存在本地時間被篡改的風(fēng)險。
- 更推薦的做法是在響應(yīng)攔截器中根據(jù)狀態(tài)碼(如401)來判斷AccessToken是否過期。
- 使用RefreshToken獲取新Token:
當(dāng)檢測到AccessToken過期時,使用RefreshToken向認(rèn)證服務(wù)器發(fā)送請求以獲取新的AccessToken和(可選的)新的RefreshToken。
將新獲取的AccessToken保存到本地緩存,并替換掉舊的AccessToken。
重新發(fā)送請求:
使用新的AccessToken重新發(fā)送之前因Token過期而失敗的請求。
這可能需要將失敗的請求暫存起來,并在獲取到新Token后依次重新發(fā)送。
四、防止多次刷新Token
- 設(shè)置一個標(biāo)志位(如isRefreshing)來指示當(dāng)前是否正在刷新Token。
- 如果在刷新Token的過程中又收到了需要刷新Token的請求,則可以直接使用已獲取的(或正在獲取的)新Token,而不是再次發(fā)起刷新Token的請求。
實現(xiàn)
前端實現(xiàn)Token無感知刷新的過程主要涉及到對HTTP請求的攔截、Token狀態(tài)的判斷、Token的刷新以及請求的重發(fā)等步驟。以下是一個詳細(xì)的實現(xiàn)流程:
一、Token的獲取與存儲
- 用戶登錄:
- 用戶輸入用戶名和密碼進行登錄。
- 登錄成功后,后端服務(wù)器會生成一個AccessToken(短期Token)和一個RefreshToken(長期Token),并將它們返回給前端。
- 存儲Token:
前端將AccessToken和RefreshToken存儲在瀏覽器的本地緩存中,如localStorage或sessionStorage。由于localStorage具有持久性,更適合存儲RefreshToken;而sessionStorage在頁面會話結(jié)束時會被清除,適合存儲AccessToken(但考慮到需要跨會話保持登錄狀態(tài),通常也會選擇localStorage)。
二、請求攔截器的設(shè)置
- 創(chuàng)建Axios實例:
- 使用Axios等HTTP客戶端庫創(chuàng)建一個Axios實例,并配置基礎(chǔ)URL、請求超時時間等。
- 設(shè)置請求攔截器:
在發(fā)送請求之前,通過請求攔截器檢查localStorage中是否存儲了有效的AccessToken。
如果存在,則將AccessToken添加到請求的Authorization頭部。
設(shè)置響應(yīng)攔截器:
在接收到響應(yīng)后,通過響應(yīng)攔截器檢查響應(yīng)狀態(tài)碼。
如果狀態(tài)碼為401(未授權(quán)),則表明AccessToken已過期,此時需要嘗試使用RefreshToken刷新Token。
三、Token刷新邏輯
- 檢查Token是否過期:
- 響應(yīng)攔截器中,根據(jù)狀態(tài)碼401判斷AccessToken是否過期。注意,更準(zhǔn)確的做法是在響應(yīng)體中包含Token過期的具體信息,但這里以狀態(tài)碼為例。
- 使用RefreshToken獲取新Token:
發(fā)起一個POST請求到認(rèn)證服務(wù)器,將RefreshToken作為請求體或請求頭發(fā)送給后端。
后端驗證RefreshToken的有效性,并返回一個新的AccessToken(和可選的新的RefreshToken)。
前端接收到新的AccessToken后,將其保存到localStorage中,并替換掉舊的AccessToken。
重新發(fā)送請求:
使用新的AccessToken重新發(fā)送之前因Token過期而失敗的請求。
這可以通過將失敗的請求暫存起來,并在獲取到新Token后依次重新發(fā)送來實現(xiàn)。
四、防止多次刷新Token
- 在刷新Token的過程中,設(shè)置一個標(biāo)志位(如isRefreshing)來指示當(dāng)前是否正在刷新Token。
- 如果在刷新Token的過程中又收到了需要刷新Token的請求,則可以直接使用已獲取的(或正在獲取的)新Token,而不是再次發(fā)起刷新Token的請求。
五、代碼示例(簡化版)
由于篇幅限制,這里只提供一個簡化的代碼示例框架:
import axios from 'axios';
// 創(chuàng)建axios實例
const service = axios.create({
baseURL: 'http://your-api-url', // API的base_url
timeout: 5000 // 請求超時時間
});
// 請求攔截器
service.interceptors.request.use(
config => {
// 從localStorage獲取token,并設(shè)置到請求頭中
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => {
// 處理請求錯誤
console.error('請求錯誤:', error); // for debug
Promise.reject(error);
}
);
// 響應(yīng)攔截器
service.interceptors.response.use(
response => {
// 對響應(yīng)數(shù)據(jù)做點什么
return response.data;
},
error => {
// 處理響應(yīng)錯誤
if (error.response && error.response.status === 401) {
// 嘗試使用RefreshToken刷新Token
return refreshToken().then(newToken => {
// 設(shè)置新的Token并重新發(fā)送請求
localStorage.setItem('token', newToken);
// 這里需要實現(xiàn)請求重發(fā)的邏輯,可以通過修改Axios實例的配置或使用其他方式
}).catch(err => {
// 刷新Token失敗,可能需要用戶重新登錄
console.error('刷新Token失敗:', err);
});
}
return Promise.reject(error);
}
);
// 刷新Token的函數(shù)(需要實現(xiàn))
function refreshToken() {
// 發(fā)送請求到認(rèn)證服務(wù)器獲取新的Token
// 返回Promise,解析為新的Token
}
上述代碼示例是一個簡化的框架,實際實現(xiàn)時需要根據(jù)具體業(yè)務(wù)需求和后端API進行相應(yīng)的調(diào)整和完善。特別是刷新Token的函數(shù)