单个接口的刷新token很好解决,难点是多接口并发
首先将token过期后的请求存到一个数组队列中,想办法让这个请求处于等待中,一直等到刷新token后再逐个重试清空请求队列。
那么如何做到让这个请求处于等待中呢?为了解决这个问题,我们得借助Promise。将请求存进队列中后,同时返回一个Promise,让这个Promise一直处于Pending状态(即不调用resolve或reject),此时这个请求就会一直等啊等,只要我们不执行resolve或reject,这个请求就会一直在等待。当刷新请求的接口返回来后,我们再调用resolve或reject,逐个重试
基于nuxt框架,$axios模块,伪代码如下:
import { Message, Loading } from "element-ui"
import Vue from "vue"
export default ({ $axios, store, app, redirect, route, req, res }) => {
let [loadingCount, currencyCode, currentLanguage, isRefreshing, token] = [
0,
"",
"",
true,
""
]
$(error => {
const code = parseInt( && )
if ( && code === 401) {
if (!token) {
if () {
goLogin()
return
}
return
}
if (isRefreshing) {
// 刷新token
refreshToken()
}
isRefreshing = false
// 将请求挂起(返回一个pending状态的promise,等待刷新接口返回结果后,再调用resolve或reject)
const retryOriginalRequest = new Promise((resolve, reject) => {
addSubscriber(token => {
if (!token) {
/* eslint-disable */
return reject("token refresh error")
/* eslint-enable */
}
if ( && token) {
const config =
= `Bearer ${token}`
$(config).then(res => {
resolve(res)
})
}
})
})
return retryOriginalRequest
}
return (error)
})
async function refreshToken(config) {
let host = ""
if () {
host = || ""
} else {
host = ""
}
const data = await $axios
.$post(`${host}/account/refresh-token`, {}, { baseURL: "" })
.catch(err => {
// 清空store里面的用户信息
("setUser", {})
// reject
onAccessTokenFetched("")
if (config) {
goLogin()
}
return (err)
})
let expireIn = ""
if (data && === 0) {
token =
expireIn = .expires_in
if () {
const stringObject =
= replaceParamVal(stringObject, "token", token)
(
"Set-Cookie",
`token=${token};Domain=.;Path=/;Max-Age=${expireIn}`
)
}
// 刷新token成功,执行数组里的函数,重新发起被挂起的请求(resolve)
onAccessTokenFetched(token)
} else {
// 清空store里面的用户信息
("setUser", {})
// reject
onAccessTokenFetched("")
if (config) {
goLogin()
}
}
// app.$("token", token)
isRefreshing = true
}
// 替换新token
function replaceParamVal(stringObject, paramName, replaceWith) {
const str = (/\s/g, "")
/* eslint-disable */
const re = eval('/('+ paramName+'=)([^;]*)/gi')
/* eslint-enable */
const newParam = (re, paramName + "=" + replaceWith)
return newParam
}
// 被挂起的请求数组
let subscribers = []
// push所有请求到数组中
function addSubscriber(callback) {
(callback)
}
// 刷新请求(subscribers数组中的请求得到新的token之后会自执行,用新的token去请求数据)
function onAccessTokenFetched(token) {
(callback => {
callback(token)
})
subscribers = []
}
function goLogin() {
if () {
redirect(`/account/login`)
} else {
= `/account/login`
}
}
}