|
@@ -2,7 +2,7 @@ import { v4 as uuid_v4 } from 'uuid'
|
|
|
import { FetchClient, type FetchResponse } from './fetchClient'
|
|
import { FetchClient, type FetchResponse } from './fetchClient'
|
|
|
import { useUserStore } from '@/stores/user'
|
|
import { useUserStore } from '@/stores/user'
|
|
|
import { jumpToSignin } from '@/utils/casdoor/casdoor'
|
|
import { jumpToSignin } from '@/utils/casdoor/casdoor'
|
|
|
-import { refreshToken } from './auth'
|
|
|
|
|
|
|
+import { refreshTokenRaw } from './auth'
|
|
|
|
|
|
|
|
const baseUrl = import.meta.env.BASE_URL
|
|
const baseUrl = import.meta.env.BASE_URL
|
|
|
|
|
|
|
@@ -18,6 +18,8 @@ const http = new FetchClient(baseUrl + 'api', 60 * 1000) // 超时时间设置
|
|
|
|
|
|
|
|
let isRefreshing = false
|
|
let isRefreshing = false
|
|
|
let pendingQueue: (() => void)[] = []
|
|
let pendingQueue: (() => void)[] = []
|
|
|
|
|
+// 是否已进入登录跳转流程,避免刷新失败时重复跳转
|
|
|
|
|
+let isRedirecting = false
|
|
|
|
|
|
|
|
async function handleTokenExpired(
|
|
async function handleTokenExpired(
|
|
|
response: FetchResponse<Response<any>>,
|
|
response: FetchResponse<Response<any>>,
|
|
@@ -42,19 +44,26 @@ async function handleTokenExpired(
|
|
|
isRefreshing = true
|
|
isRefreshing = true
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // 调用刷新接口
|
|
|
|
|
- const res = await refreshToken()
|
|
|
|
|
|
|
+ // 调用刷新接口(绕过业务拦截器,失败会抛出带 status 的错误)
|
|
|
|
|
+ const res = await refreshTokenRaw()
|
|
|
|
|
+
|
|
|
|
|
+ if (!res) {
|
|
|
|
|
+ // 刷新接口返回空 token,视为刷新失败
|
|
|
|
|
+ throw new Error('Empty token from refresh')
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
const userStore = useUserStore()
|
|
const userStore = useUserStore()
|
|
|
userStore.setToken(res)
|
|
userStore.setToken(res)
|
|
|
|
|
|
|
|
// 刷新完成,重放队列请求
|
|
// 刷新完成,重放队列请求
|
|
|
- await Promise.all(pendingQueue.map((fn) => fn()))
|
|
|
|
|
|
|
+ const queue = pendingQueue
|
|
|
pendingQueue = []
|
|
pendingQueue = []
|
|
|
|
|
+ await Promise.all(queue.map((fn) => fn()))
|
|
|
|
|
|
|
|
// 重新执行当前请求
|
|
// 重新执行当前请求
|
|
|
return originalRequest()
|
|
return originalRequest()
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
|
|
+ // 刷新失败:清理并跳转登录,抛出错误让上层感知(不再用已清空的 token 重放请求)
|
|
|
handleRefreshFailed()
|
|
handleRefreshFailed()
|
|
|
throw err
|
|
throw err
|
|
|
} finally {
|
|
} finally {
|
|
@@ -68,6 +77,12 @@ function handleRefreshFailed() {
|
|
|
pendingQueue = []
|
|
pendingQueue = []
|
|
|
isRefreshing = false
|
|
isRefreshing = false
|
|
|
|
|
|
|
|
|
|
+ // 保证只跳转一次,避免多个并发请求同时触发跳转
|
|
|
|
|
+ if (isRedirecting) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ isRedirecting = true
|
|
|
|
|
+
|
|
|
console.log('jumpToSignin')
|
|
console.log('jumpToSignin')
|
|
|
|
|
|
|
|
// 跳转登录页
|
|
// 跳转登录页
|