Vue中如何處理token過(guò)期問(wèn)題
后端為了安全,token一般存在有效時(shí)間,當(dāng)token過(guò)期,所有請(qǐng)求失效
解決方案
方案一
在請(qǐng)求發(fā)起前攔截每個(gè)請(qǐng)求,判斷token的有效時(shí)間是否已經(jīng)過(guò)期,若已過(guò)期,則將請(qǐng)求掛起,先刷新token后再繼續(xù)請(qǐng)求。
- 優(yōu)點(diǎn): 在請(qǐng)求前攔截,能節(jié)省請(qǐng)求,省流量
- 缺點(diǎn): 需要后端額外提供一個(gè)token過(guò)期時(shí)間的字段;使用了本地時(shí)間判斷,若本地時(shí)間被篡改,特別是本地時(shí)間比服務(wù)器時(shí)間慢時(shí),攔截會(huì)失敗
- 使用方法:axios.interceptors.request.use() 這個(gè)請(qǐng)求前攔截方法
方案二
根據(jù)攔截返回后的數(shù)據(jù)判斷,若token過(guò)期,先刷新token,再進(jìn)行一次請(qǐng)求。
- 優(yōu)點(diǎn):不需額外的token過(guò)期字段,不需判斷時(shí)間
- 缺點(diǎn): 會(huì)消耗多一次請(qǐng)求,耗流量
- 使用方法:axios.interceptors.response.use() 這個(gè)響應(yīng)攔截方法
最簡(jiǎn)單方法:獲取到過(guò)期code,直接跳到登錄頁(yè)
方案三:封裝axios基本結(jié)構(gòu)
1、token是存在localStorage中
//在request.js
?
import axios from 'axios'
?
// 創(chuàng)建一個(gè)實(shí)例
const service = axios.create({
? ? baseURL: process.env.VUE_APP_BASE_API,?
? ? timeout: 5000 // request timeout
})
?
// 從localStorage中獲取token
function getLocalToken () {
? ? const token = window.localStorage.getItem('token')
? ? return token
}
?
// 給實(shí)例添加一個(gè)setToken方法,用于登錄后將最新token動(dòng)態(tài)添加到header,同時(shí)將token保存在localStorage中
service.setToken = (token) => {
? instance.defaults.headers['X-Token'] = token
? window.localStorage.setItem('token', token)
}
?
// 攔截返回的數(shù)據(jù)
service.interceptors.response.use(response => {
? // 接下來(lái)會(huì)在這里進(jìn)行token過(guò)期的邏輯處理
? return response
}, error => {
? return Promise.reject(error)
})
?
//暴露
export default service假如后端接口token過(guò)期返回的code是401
//獲取新的token請(qǐng)求
function refreshToken () {
? ? return service.post('/refreshtoken').then(res => res.data)
}
?
// 攔截返回的數(shù)據(jù)
service.interceptors.response.use(response => {
? // 接下來(lái)會(huì)在這里進(jìn)行token過(guò)期的邏輯處理
? const { code } = response.data
? ?? ?-----------------------------------------------------------
? ?? ?// 說(shuō)明token過(guò)期了,獲取新的token
?? ? if (code === 401) {
?? ??? ?return refreshToken().then(res => {
?? ??? ?// 刷新token成功,將最新的token更新到header中,同時(shí)保存在localStorage中
?? ? ? ? ?const { token } = res.data
?? ? ? ? ?service.setToken(token)
?? ? ? ? ?
?? ? ? ? ?// 獲取當(dāng)前失敗的請(qǐng)求
?? ? ? ? ?const config = response.config
?? ? ? ? ?//重置失敗請(qǐng)求的配置
?? ? ? ? ?config.headers['X-Token'] = token
?? ? ? ? ?config.baseURL = '' "
?? ? ? ? ?//重試當(dāng)前請(qǐng)求并返回promise
?? ? ? ? ?return service(config)
?? ??? ?}).catch( res=>{
?? ??? ??? ?//重新請(qǐng)求token失敗,跳轉(zhuǎn)到登錄頁(yè)
?? ??? ??? ?window.location.href = '/login '
?? ??? ?} )
?? ? }
?? ? --------------------------------------------------------------
? return response
?
}, error => {
? return Promise.reject(error)
})2、問(wèn)題和優(yōu)化
如果token失效時(shí),存在多個(gè)請(qǐng)求,這就會(huì)導(dǎo)致多次執(zhí)行刷新token的接口
在request.js中用一個(gè)變量來(lái)標(biāo)記當(dāng)前是否正在刷新token的狀態(tài),如果正在刷新則不再調(diào)用刷新token的接口
在request.js
// 是否正在刷新的標(biāo)記
let isRefreshing = false
?
-----------------------------------------------------------
? ?? ?// 說(shuō)明token過(guò)期了,獲取新的token
?? ? if (code === 401) {
?? ? ?? ?//判斷一下狀態(tài)
?? ??? ?if( !isRefreshing ){
?? ??? ??? ?//修改狀態(tài),進(jìn)入更新token階段
?? ??? ??? ?isRefreshing = true
?? ??? ??? ?
?? ??? ??? ?return refreshToken().then(res => {
?? ??? ??? ?// 刷新token成功,將最新的token更新到header中,同時(shí)保存在localStorage中
?? ??? ? ? ? ?const { token } = res.data
?? ??? ? ? ? ?service.setToken(token)
?? ??? ? ? ? ?
?? ??? ? ? ? ?// 獲取當(dāng)前失敗的請(qǐng)求
?? ??? ? ? ? ?const config = response.config
?? ??? ? ? ? ?//重置失敗請(qǐng)求的配置
?? ??? ? ? ? ?config.headers['X-Token'] = token
?? ??? ? ? ? ?config.baseURL = '' "
?? ??? ? ? ? ?//重試當(dāng)前請(qǐng)求并返回promise
?? ??? ? ? ? ?return service(config)
?? ??? ??? ?}).catch( res=>{
?? ??? ??? ??? ?//重新請(qǐng)求token失敗,跳轉(zhuǎn)到登錄頁(yè)
?? ??? ??? ??? ?window.location.href = '/login '
?? ??? ??? ?} ).finally( ()=>{
?? ??? ??? ??? ?//完成之后在關(guān)閉狀態(tài)
?? ??? ??? ??? ?isRefreshing = false
?? ??? ??? ?} )
?? ??? ?}
?? ? }同時(shí)發(fā)起兩個(gè)或以上的請(qǐng)求時(shí),其他接口如何重試
兩個(gè)接口幾乎同時(shí)發(fā)起和返回,第一個(gè)接口會(huì)進(jìn)入刷新token后重試的流程,而第二個(gè)接口需要先存起來(lái),然后等刷新token后再重試。同樣,如果同時(shí)發(fā)起三個(gè)請(qǐng)求,此時(shí)需要緩存后兩個(gè)接口,等刷新token后再重試;
當(dāng)?shù)诙€(gè)過(guò)期的請(qǐng)求進(jìn)來(lái),token正在刷新,我們先將這個(gè)請(qǐng)求存到一個(gè)數(shù)組隊(duì)列中,想辦法讓這個(gè)請(qǐng)求處于等待中,一直等到刷新token后再逐個(gè)重試清空請(qǐng)求隊(duì)列。
將請(qǐng)求存進(jìn)隊(duì)列中后,同時(shí)返回一個(gè)Promise,讓這個(gè)Promise一直處于Pending狀態(tài)(即不調(diào)用resolve),此時(shí)這個(gè)請(qǐng)求就會(huì)一直等啊等,只要我們不執(zhí)行resolve,這個(gè)請(qǐng)求就會(huì)一直在等待。當(dāng)刷新請(qǐng)求的接口返回來(lái)后,我們?cè)僬{(diào)用resolve,逐個(gè)重試。
// 是否正在刷新的標(biāo)記
let isRefreshing = false
// 重試隊(duì)列,每一項(xiàng)將是一個(gè)待執(zhí)行的函數(shù)形式
let requests = []
?
-----------------------------------------------------------
? ?? ?// 說(shuō)明token過(guò)期了,獲取新的token
?? ? if (code === 401) {
?? ? ?? ?const config = response.config
?? ? ?? ?//判斷一下狀態(tài)
?? ??? ?if( !isRefreshing ){
?? ??? ??? ?//修改狀態(tài),進(jìn)入更新token階段
?? ??? ??? ?isRefreshing = true
?? ??? ??? ?// 獲取當(dāng)前的請(qǐng)求
?? ??? ??? ?return refreshToken().then(res => {
?? ??? ??? ?// 刷新token成功,將最新的token更新到header中,同時(shí)保存在localStorage中
?? ??? ? ? ? ?const { token } = res.data
?? ??? ? ? ? ?service.setToken(token)
?? ??? ? ? ? ?
?? ??? ? ? ? ?//重置失敗請(qǐng)求的配置
?? ??? ? ? ? ?config.headers['X-Token'] = token
?? ??? ? ? ? ?config.baseURL = '' "
?? ??? ? ? ? ?
?? ??? ? ? ? ?//已經(jīng)刷新了token,將所有隊(duì)列中的請(qǐng)求進(jìn)行重試
?? ??? ? ? ? ?requests.forEach(cb => cb(token))
?? ??? ? ? ? ?// 重試完了別忘了清空這個(gè)隊(duì)列
?? ??? ? ? ? ?requests = []
?? ??? ? ? ? ?
?? ??? ? ? ? ?return service(config)
?? ??? ??? ?}).catch( res=>{
?? ??? ??? ??? ?//重新請(qǐng)求token失敗,跳轉(zhuǎn)到登錄頁(yè)
?? ??? ??? ??? ?window.location.href = '/login '
?? ??? ??? ?} ).finally( ()=>{
?? ??? ??? ??? ?//完成之后在關(guān)閉狀態(tài)
?? ??? ??? ??? ?isRefreshing = false
?? ??? ??? ?} )
?? ??? ?} else{
?? ??? ??? ? // 正在刷新token,返回一個(gè)未執(zhí)行resolve的promise
?? ??? ??? ? return new Promise((resolve) => {
?? ? ? ? ? ? // 將resolve放進(jìn)隊(duì)列,用一個(gè)函數(shù)形式來(lái)保存,等token刷新后直接執(zhí)行
?? ??? ? ? ? ? ? requests.push((token) => {
?? ??? ??? ? ? ? ? ? ?config.baseURL = ''
?? ??? ??? ? ? ? ? ? ?config.headers['X-Token'] = token
?? ??? ??? ? ? ? ? ? ?resolve(instance(config))
?? ??? ? ? ? ? ?? ? })
?? ? ? ? ?? ? })
?? ??? ?}
?? ? }3、完整版
//在request.js
?
import axios from 'axios'
?
// 是否正在刷新的標(biāo)記
let isRefreshing = false
// 重試隊(duì)列,每一項(xiàng)將是一個(gè)待執(zhí)行的函數(shù)形式
let requests = []
?
// 創(chuàng)建一個(gè)實(shí)例
const service = axios.create({
? ? baseURL: process.env.VUE_APP_BASE_API,?
? ? timeout: 5000 // request timeout
})
?
// 從localStorage中獲取token
function getLocalToken () {
? ? const token = window.localStorage.getItem('token')
? ? return token
}
?
// 給實(shí)例添加一個(gè)setToken方法,用于登錄后將最新token動(dòng)態(tài)添加到header,同時(shí)將token保存在localStorage中
service.setToken = (token) => {
? instance.defaults.headers['X-Token'] = token
? window.localStorage.setItem('token', token)
}
?
//獲取新的token請(qǐng)求
function refreshToken () {
? ? return service.post('/refreshtoken').then(res => res.data)
}
?
// 攔截返回的數(shù)據(jù)
service.interceptors.response.use(response => {
? // 接下來(lái)會(huì)在這里進(jìn)行token過(guò)期的邏輯處理
? const { code } = response.data
?? ?-----------------------------------------------------------
? ?? ?// 說(shuō)明token過(guò)期了,獲取新的token
?? ? if (code === 401) {
?? ? ?? ?const config = response.config
?? ? ?? ?//判斷一下狀態(tài)
?? ??? ?if( !isRefreshing ){
?? ??? ??? ?//修改狀態(tài),進(jìn)入更新token階段
?? ??? ??? ?isRefreshing = true
?? ??? ??? ?// 獲取當(dāng)前的請(qǐng)求
?? ??? ??? ?return refreshToken().then(res => {
?? ??? ??? ?// 刷新token成功,將最新的token更新到header中,同時(shí)保存在localStorage中
?? ??? ? ? ? ?const { token } = res.data
?? ??? ? ? ? ?service.setToken(token)
?? ??? ? ? ? ?
?? ??? ? ? ? ?//重置失敗請(qǐng)求的配置
?? ??? ? ? ? ?config.headers['X-Token'] = token
?? ??? ? ? ? ?config.baseURL = '' "
?? ??? ? ? ? ?
?? ??? ? ? ? ?//已經(jīng)刷新了token,將所有隊(duì)列中的請(qǐng)求進(jìn)行重試
?? ??? ? ? ? ?requests.forEach(cb => cb(token))
?? ??? ? ? ? ?// 重試完了別忘了清空這個(gè)隊(duì)列
?? ??? ? ? ? ?requests = []
?? ??? ? ? ? ?
?? ??? ? ? ? ?return service(config)
?? ??? ??? ?}).catch( res=>{
?? ??? ??? ??? ?//重新請(qǐng)求token失敗,跳轉(zhuǎn)到登錄頁(yè)
?? ??? ??? ??? ?window.location.href = '/login '
?? ??? ??? ?} ).finally( ()=>{
?? ??? ??? ??? ?//完成之后在關(guān)閉狀態(tài)
?? ??? ??? ??? ?isRefreshing = false
?? ??? ??? ?} )
?? ??? ?} else{
?? ??? ??? ? // 正在刷新token,返回一個(gè)未執(zhí)行resolve的promise
?? ??? ??? ? return new Promise((resolve) => {
?? ? ? ? ? ? // 將resolve放進(jìn)隊(duì)列,用一個(gè)函數(shù)形式來(lái)保存,等token刷新后直接執(zhí)行
?? ??? ? ? ? ? ? requests.push((token) => {
?? ??? ??? ? ? ? ? ? ?config.baseURL = ''
?? ??? ??? ? ? ? ? ? ?config.headers['X-Token'] = token
?? ??? ??? ? ? ? ? ? ?resolve(instance(config))
?? ??? ? ? ? ? ?? ? })
?? ? ? ? ?? ? })
?? ??? ?}
?? ? }
?? ? --------------------------------------------------------------
? return response
}, error => {
? return Promise.reject(error)
})
//暴露
export default service以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則
這篇文章主要給大家介紹了關(guān)于vue前端el-input輸入限制輸入位數(shù)及輸入規(guī)則的相關(guān)資料,文中通過(guò)代碼介紹的介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
Vue Element校驗(yàn)validate的實(shí)例
這篇文章主要介紹了Vue Element校驗(yàn)validate的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
解決vue中el-date-picker?type=daterange日期不回顯的問(wèn)題
這篇文章主要介紹了解決vue中el-date-picker?type=daterange日期不回顯的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Vue響應(yīng)式添加、修改數(shù)組和對(duì)象的值
有些時(shí)候,不得不想添加、修改數(shù)組和對(duì)象的值,但是直接添加、修改后又失去了getter、setter,由于JavaScript的限制, Vue不能檢測(cè)以部分變動(dòng)的數(shù)組2017-03-03
在Vue項(xiàng)目中使用snapshot測(cè)試的具體使用
這篇文章主要介紹了在Vue項(xiàng)目中使用snapshot測(cè)試的具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04
使用Vue3實(shí)現(xiàn)列表虛擬滾動(dòng)的詳細(xì)步驟
在前端開發(fā)中,列表的虛擬滾動(dòng)是一種常見的優(yōu)化手段,可以大大提升頁(yè)面性能,在Vue3中,我們可以通過(guò)一些技巧來(lái)實(shí)現(xiàn)列表的虛擬滾動(dòng),本文將介紹如何使用Vue3實(shí)現(xiàn)列表的虛擬滾動(dòng),讓你的頁(yè)面加載更快、更流暢,需要的朋友可以參考下2024-09-09
vue實(shí)現(xiàn)token過(guò)期自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面
本文主要介紹了vue實(shí)現(xiàn)token過(guò)期自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
Vue3+TypeScript實(shí)現(xiàn)二維碼生成組件
在?Web?應(yīng)用中,生成二維碼是常見的需求,本文介紹如何用?Vue3?和?TypeScript?開發(fā)一個(gè)二維碼生成組件,支持生成圖片或?canvas?形式的二維碼,并提供豐富的配置選項(xiàng),感興趣的小伙伴跟著小編一起來(lái)看看吧2024-04-04

