前端雙token無感刷新圖文詳解
1.雙token的使用場景
眾所周知,Token
作為用戶獲取受保護(hù)資源的憑證,必須設(shè)置一個過期時間,否則一次登錄便可永久使用,認(rèn)證功能就失去了意義。但是矛盾在于:過期時間設(shè)置得太長,用戶數(shù)據(jù)的安全性將大打折扣;過期時間設(shè)置得太短,用戶就必須每隔一段時間重新登錄,以獲取新的憑證,這會極大挫傷用戶的積極性。
1.假如你正在訪問某個平臺,沉浸式的使用了一小時后卻突然彈出一個token過期,重定向到了登錄頁…
2.假如你正在某網(wǎng)站里填寫你的個人信息或者內(nèi)容繁多的表單選項(xiàng),填到一半的時候token過期了…提示需要重新登錄,結(jié)果登錄回來之后又要重新填寫表單信息…T~T
3.假如你正在激情的搶票、蹲卡點(diǎn)秒殺活動!結(jié)果剛要搶購卻因token過期被重定向到登錄頁…
是不是代入起來就已經(jīng)很頭疼了?那如何避免這種情況發(fā)生呢,接下來我們就引入今天的主角——無感刷新token
2.什么是token
在介紹雙token無感刷新
之前,我們先簡單介紹一下什么是token
吧!
token是一種用戶標(biāo)識,表示用戶身份,類似于我們的身份證件。Token
是在服務(wù)端產(chǎn)生的。如果前端使用用戶名/密碼向服務(wù)端請求認(rèn)證,服務(wù)端認(rèn)證成功,那么在服務(wù)端會返回 Token
給前端。前端可以在每次請求的時候帶上 Token
證明自己的合法地位。如果這個 Token
在服務(wù)端持久化(比如存入數(shù)據(jù)庫),那它就是一個永久的身份令牌。
3.如何無感刷新token(圖文+代碼)
當(dāng)token
過期了之后,我們應(yīng)該如何做到讓用戶無感知的自動刷新token
呢?
目前比較常見的有三種方法:
- 寫個定時器,時間一到就刷新
getToken
接口來獲取新token
(極其不推薦):消耗性能,不斷的去發(fā)送網(wǎng)絡(luò)請求,不建議這種做法 - 后端返回一個過期時間,前端每次發(fā)送請求都去判斷token是否過期,如果過期就調(diào)用
getToken
接口來獲取新token
(不推薦):需要后端額外提供一個過期時間的字段;使用了本地時間判斷,若本地時間被篡改,特別是本地時間比服務(wù)器時間慢時,攔截會失敗。 - 在請求響應(yīng)攔截器中攔截,判斷
token
返回過期后,調(diào)用刷新token接口。(推薦???):無性能損耗
在登錄成功后,后端會返回兩個Token
,一個是Access Token
(即短token),一個是Refresh Token
(即長token)。前端需要將這兩個Token
保存到本地存儲中,例如localStorage
或sessionStorage
中,以便在需要時使用。
當(dāng)需要訪問API時,前端將從本地存儲中獲取Access Token
,并將其放入請求頭中發(fā)送到后端。如果Access Token
過期了,后端會返回一個錯誤響應(yīng),并提示前端進(jìn)行刷新Token
的操作。
前端可以使用下面的代碼實(shí)現(xiàn)刷新Token的操作:
import axios from "axios"; import { getToken, setToken, setRefreshToken, getRefreshToken } from "./token"; import { ElMessage } from "element-plus"; import router from "@/router"; import { refreshToken } from "./refreshtoken"; const service = axios.create({ baseURL: "http://localhost:8087", headers: { Authorization: `Bearer ${getToken()}`, }, }); // 添加請求攔截器 service.interceptors.request.use( function (config) { if (config.url === "/refresh_token") { config.headers["Authorization"] = `Bearer ${getRefreshToken()}`; } // 在發(fā)送請求之前做些什么 return config; }, function (error) { // 對請求錯誤做些什么 return Promise.reject(error); } ); // 添加響應(yīng)攔截器 service.interceptors.response.use(async (res) => { // 1.第一次登錄了以后 后臺在header里面返回了短token 那么要先接收token存儲到localstorage里面 if (res.headers.authorization) { const token = res.headers.authorization.replace("Bearer ", ""); // 設(shè)置短token setToken(token); service.defaults.headers.Authorization = `Bearer ${token}`; } // 2. 第一次登錄了以后 后臺在header里面返回了長token 那么要先接受長token 存儲到localstorage里面 if (res.headers.refreshtoken) { const refreshtoken = res.headers.refreshtoken.replace("Bearer ", ""); // 設(shè)置長token setRefreshToken(refreshtoken); } // 3 假設(shè)當(dāng)后端返回401的時候 代表token失效 時間到了 這個時候正常處理就是跳到登錄頁面 // 但是在實(shí)際的業(yè)務(wù)場景的時候 用戶體驗(yàn)非常不好 if (res.data.code === 401) { // ElMessage({ // message: "token失效", // type: "warning", // }); // router.push("/login"); // 這個地方就是短token失效了 提交表單 不跳到登錄頁面 因?yàn)檫@樣用戶體驗(yàn)很不好 // 請求剛剛定義的一個接口 // 這個時候 提交表單的接口 還沒提交 停在這里了 現(xiàn)在要干啥?請求 // 干啥了? 請求了changtoken攜帶過去 刷新token的接口 const success = await refreshToken(); if (success) { res.config.headers.Authorization = `Bearer ${getToken()}`; const result = await service.request(res.config); return result; } } return res.data; }); function request(options) { options.method = options.method || "get"; // 關(guān)于get請求參數(shù)的調(diào)整 if (options.method.toLowerCase() === "get") { options.params = options.data; } return service(options); } export default request;
對應(yīng)的token.js
和refreshtoken.js
代碼:
模擬網(wǎng)卡的時候,響應(yīng)數(shù)據(jù)還沒返回就重復(fù)刷新的情況:(本質(zhì)就是借助Promise。將請求存進(jìn)隊(duì)列中后,同時返回一個Promise,讓這個Promise一直處于Pending狀態(tài)(即不調(diào)用resolve),此時這個請求就會一直等啊等,只要我們不執(zhí)行resolve,這個請求就會一直在等待。當(dāng)刷新請求的接口返回來后,我們再調(diào)用resolve,逐個重試。)
將token
存進(jìn)localstorage
里,同時為了防止多次刷新
使用 Token
和 Refresh Token
的時序圖如下:
1)登錄
2)業(yè)務(wù)請求
3)Token 過期,刷新 Token
現(xiàn)在,我們就已經(jīng)成功實(shí)現(xiàn)了長短token的無感刷新啦!
一定要理解文章開頭這張圖的流程哦
總結(jié)
到此這篇關(guān)于前端雙token無感刷新的文章就介紹到這了,更多相關(guān)前端雙token無感刷新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
three.js實(shí)現(xiàn)炫酷的全景3D重力感應(yīng)
這篇文章主要為大家詳細(xì)介紹了three.js實(shí)現(xiàn)炫酷的全景3D重力感應(yīng),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12javascript ES6 Template String模板字符串使用方法
這篇文章主要介紹了javascript ES6 模板字符串(Template String)是增強(qiáng)版的字符串,用反引號(`)標(biāo)識,它可以當(dāng)作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量,需要的朋友可以參考下2023-06-06BootStrap實(shí)現(xiàn)輪播圖效果(收藏)
這篇文章主要介紹了BootStrap實(shí)現(xiàn)輪播圖效果,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-12-12微信公眾平臺開發(fā)教程(六)獲取個性二維碼的實(shí)例
二維碼的用處有很多,本篇文章主要介紹了微信公眾平臺開發(fā)教程(六)獲取個性二維碼的實(shí)例,有興趣的可以了解一下。2016-12-12javascript實(shí)現(xiàn)控制的多級下拉菜單
這篇文章主要介紹了javascript實(shí)現(xiàn)控制的多級下拉菜單,包含示例代碼,效果非常不錯,這里推薦給大家。2015-07-07利用JavaScript編寫一個花里胡哨的點(diǎn)擊按鈕
這篇文章主要介紹了如何利用HTML+CSS+JavaScript制作一個花里胡哨的點(diǎn)擊按鈕。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03