Browse Source

fix: 修复当`token`过期后,如果页面有多个请求会重复刷新`token`

xiaoxian521 2 years ago
parent
commit
27bc135bd2
3 changed files with 50 additions and 20 deletions
  1. 2 2
      mock/refreshToken.ts
  2. 5 0
      src/utils/auth.ts
  3. 43 18
      src/utils/http/index.ts

+ 2 - 2
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"
           }

+ 5 - 0
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;
+};

+ 43 - 18
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);
               }
             });
       },