From 27bc135bd2b19553a4ddd2a9809fb4800ee7208c Mon Sep 17 00:00:00 2001 From: xiaoxian521 <1923740402@qq.com> Date: Tue, 1 Nov 2022 16:06:22 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=BD=93`token`?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E5=90=8E=EF=BC=8C=E5=A6=82=E6=9E=9C=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=9C=89=E5=A4=9A=E4=B8=AA=E8=AF=B7=E6=B1=82=E4=BC=9A?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E5=88=B7=E6=96=B0`token`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/refreshToken.ts | 4 +-- src/utils/auth.ts | 5 ++++ src/utils/http/index.ts | 61 +++++++++++++++++++++++++++++------------ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/mock/refreshToken.ts b/mock/refreshToken.ts index dfdb711f5..87b89953e 100644 --- a/mock/refreshToken.ts +++ b/mock/refreshToken.ts @@ -10,8 +10,8 @@ export default [ return { success: true, data: { - accessToken: "eyJhbGciOiJIUzUxMiJ9.admin", - refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh", + accessToken: "eyJhbGciOiJIUzUxMiJ9.newAdmin", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.newAdminRefresh", // `expires`选择这种日期格式是为了方便调试,后端直接设置时间戳或许更方便(每次都应该递增)。如果后端返回的是时间戳格式,前端开发请来到这个目录`src/utils/auth.ts`,把第`38`行的代码换成expires = data.expires即可。 expires: "2023/10/30 23:59:59" } diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 4aa28cc1e..2233923be 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -70,3 +70,8 @@ export function removeToken() { Cookies.remove(TokenKey); sessionStorage.removeItem(sessionKey); } + +/** 格式化token(jwt格式) */ +export const formatToken = (token: string): string => { + return "Bearer " + token; +}; diff --git a/src/utils/http/index.ts b/src/utils/http/index.ts index 98b7f367a..fc98af1e6 100644 --- a/src/utils/http/index.ts +++ b/src/utils/http/index.ts @@ -12,7 +12,7 @@ import { import { stringify } from "qs"; import NProgress from "../progress"; // import { loadEnv } from "@build/index"; -import { getToken } from "@/utils/auth"; +import { getToken, formatToken } from "@/utils/auth"; import { useUserStoreHook } from "@/store/modules/user"; // 加载环境变量 VITE_PROXY_DOMAIN(开发环境) VITE_PROXY_DOMAIN_REAL(打包后的线上环境) @@ -43,27 +43,43 @@ class PureHttp { this.httpInterceptorsRequest(); this.httpInterceptorsResponse(); } + + /** token过期后,暂存待执行的请求 */ + private static requests = []; + + /** 防止重复刷新token */ + private static isRefreshing = false; + /** 初始化配置对象 */ private static initConfig: PureHttpRequestConfig = {}; /** 保存当前Axios实例对象 */ private static axiosInstance: AxiosInstance = Axios.create(defaultConfig); + /** 重连原始请求 */ + private static retryOriginalRequest(config: PureHttpRequestConfig) { + return new Promise(resolve => { + PureHttp.requests.push((token: string) => { + config.headers["Authorization"] = formatToken(token); + resolve(config); + }); + }); + } + /** 请求拦截 */ private httpInterceptorsRequest(): void { PureHttp.axiosInstance.interceptors.request.use( async (config: PureHttpRequestConfig) => { - const $config = config; // 开启进度条动画 NProgress.start(); // 优先判断post/get等方法是否传入回掉,否则执行初始化设置等回掉 if (typeof config.beforeRequestCallback === "function") { - config.beforeRequestCallback($config); - return $config; + config.beforeRequestCallback(config); + return config; } if (PureHttp.initConfig.beforeRequestCallback) { - PureHttp.initConfig.beforeRequestCallback($config); - return $config; + PureHttp.initConfig.beforeRequestCallback(config); + return config; } /** 请求白名单,放置一些不需要token的接口(通过设置请求白名单,防止token过期后再请求造成的死循环问题) */ const whiteList = ["/refreshToken", "/login"]; @@ -75,21 +91,30 @@ class PureHttp { const now = new Date().getTime(); const expired = parseInt(data.expires) - now <= 0; if (expired) { - // token过期刷新 - useUserStoreHook() - .handRefreshToken({ refreshToken: data.refreshToken }) - .then(res => { - config.headers["Authorization"] = - "Bearer " + res.data.accessToken; - resolve($config); - }); + if (!PureHttp.isRefreshing) { + PureHttp.isRefreshing = true; + // token过期刷新 + useUserStoreHook() + .handRefreshToken({ refreshToken: data.refreshToken }) + .then(res => { + const token = res.data.accessToken; + config.headers["Authorization"] = formatToken(token); + PureHttp.requests.forEach(cb => cb(token)); + PureHttp.requests = []; + }) + .finally(() => { + PureHttp.isRefreshing = false; + }); + } + resolve(PureHttp.retryOriginalRequest(config)); } else { - config.headers["Authorization"] = - "Bearer " + data.accessToken; - resolve($config); + config.headers["Authorization"] = formatToken( + data.accessToken + ); + resolve(config); } } else { - resolve($config); + resolve(config); } }); },