适用场景
- 系统有一个较长时间(7天)的
token - 当
token过期时,直接返回到登录页面, - 如果
token没有过期,达到了业务上定义的过期(距离token过期还有3天)
总结:有点类似于更新双token的长refresh_token
不适用场景
token过期时间较短(1小时-2小时)甚至更短;这个时候建议双token实现;一个短access_token,一个长refresh_token
实现方案
axios 请求拦截器
import { RefreshTokenUtil } from '@/refreshToken';
this.axiosInstance.interceptors.request.use(async (config) => {
// 请求之前处理config
const token = await RefreshTokenUtil.getToken(config.url);
if (token) {
config.headers.Authorization = token
}
return config;
}, undefined)
RefreshToken 核心类
import dayjs from 'dayjs'
import { useUserStoreWithOut } from '@/store/modules/user';
import { refreshTokenApi, REFRESH_TOKEN_URL } from '@/api/web/user'
class RefreshToken {
constructor() {
this.queue = [];
this.refreshing = false;
}
async getToken(url) {
const userStore = useUserStoreWithOut();
if (url.endsWith(REFRESH_TOKEN_URL)) {
return userStore.getToken
}
if (this.needRefresh()) {
return this.enqueueRequest();
}
return userStore.getToken
}
needRefresh() {
const userStore = useUserStoreWithOut();
if (!userStore.getToken || !userStore.getExpiresAt) {
return false
}
const expiresAt = dayjs(userStore.getExpiresAt);
const now = dayjs();
if (expiresAt.isBefore(now)) {
return false
}
return expiresAt.isBefore(now.add(6, 'day'))
}
enqueueRequest() {
return new Promise((resolve, reject) => {
this.queue.push({ resolve, reject });
if (!this.refreshing) {
this.refreshToken();
}
});
}
async refreshToken() {
try {
this.refreshing = true
const { expires_at, access_token } = await refreshTokenApi()
const userStore = useUserStoreWithOut();
userStore.setToken(access_token)
userStore.setExpiresAt(expires_at)
while (this.queue.length > 0) {
const { resolve } = this.queue.shift();
resolve(access_token)
}
} catch (error) {
console.log(error)
} finally{
this.processQueue()
this.refreshing = false
}
}
processQueue() {
const userStore = useUserStoreWithOut();
while (this.queue.length > 0) {
const { resolve } = this.queue.shift();
resolve(userStore.getToken)
}
}
}
export const RefreshTokenUtil = new RefreshToken();
pinia 的 user 模块
import { defineStore } from 'pinia'
import { store } from '@/store'
export const useUserStore = defineStore('user', {
state: () => {
return {
token: null,
expiresAt: ''
}
},
getters: {
getToken() {
return this.token || ''
},
getExpiresAt() {
return this.expiresAt
}
},
actions: {
setToken(token) {
this.token = token
},
setExpiresAt(expiresAt = '') {
this.expiresAt = expiresAt
}
},
persist: true,
})
// Need to be used outside the setup
export function useUserStoreWithOut() {
return useUserStore(store);
}
api 下的 user 模块
import { defHttp } from '@/utils/http/axios'
export const REFRESH_TOKEN_URL = 'refresh-token-api'
export const refreshTokenApi = () => {
return defHttp.get({
url: REFRESH_TOKEN_URL
})
}
👏👏👏恭喜~ 到这里就结束了。