mirror of
https://github.com/KwiTsukasa/kt-template-online-playground.git
synced 2026-05-28 00:47:42 +08:00
fix(playground): 修复 401 鉴权恢复跳转
This commit is contained in:
parent
e28c425603
commit
d1812d3c3d
@ -17,6 +17,7 @@ const ACCESS_CODES_KEY = 'kt-admin-access-codes'
|
|||||||
const USER_INFO_KEY = 'kt-admin-user-info'
|
const USER_INFO_KEY = 'kt-admin-user-info'
|
||||||
|
|
||||||
let refreshPromise: Promise<string | null> | null = null
|
let refreshPromise: Promise<string | null> | null = null
|
||||||
|
let redirectingToAdminLogin = false
|
||||||
|
|
||||||
function getApiBase() {
|
function getApiBase() {
|
||||||
return import.meta.env.VITE_APP_API_BASE || '/api'
|
return import.meta.env.VITE_APP_API_BASE || '/api'
|
||||||
@ -76,6 +77,9 @@ function buildAdminLoginUrl(redirect: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function redirectToAdminLogin() {
|
export function redirectToAdminLogin() {
|
||||||
|
if (redirectingToAdminLogin) return
|
||||||
|
|
||||||
|
redirectingToAdminLogin = true
|
||||||
window.location.href = buildAdminLoginUrl(window.location.href)
|
window.location.href = buildAdminLoginUrl(window.location.href)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,10 +8,19 @@ import {
|
|||||||
|
|
||||||
export type ApiResponse<T = unknown> = {
|
export type ApiResponse<T = unknown> = {
|
||||||
code: number
|
code: number
|
||||||
|
message?: string
|
||||||
msg: string
|
msg: string
|
||||||
data: T
|
data: T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AuthRetryConfig = AxiosRequestConfig & {
|
||||||
|
_authRetried?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthHeaderMap = Record<string, unknown> & {
|
||||||
|
get?: (name: string) => unknown
|
||||||
|
}
|
||||||
|
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
baseURL: import.meta.env.VITE_APP_API_BASE || '/api',
|
baseURL: import.meta.env.VITE_APP_API_BASE || '/api',
|
||||||
timeout: 1000 * 30,
|
timeout: 1000 * 30,
|
||||||
@ -40,14 +49,61 @@ request.interceptors.request.use(async (config) => {
|
|||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function getAuthErrorMessage(data?: Partial<ApiResponse>) {
|
||||||
|
return data?.msg || data?.message || '登录已过期'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequestAuthorization(config?: AuthRetryConfig) {
|
||||||
|
const headers = config?.headers as AuthHeaderMap | undefined
|
||||||
|
|
||||||
|
if (!headers) return null
|
||||||
|
if (typeof headers.get === 'function') {
|
||||||
|
return headers.get('Authorization') || headers.get('authorization')
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers.Authorization || headers.authorization
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retryRequestWithFreshToken(config?: AuthRetryConfig) {
|
||||||
|
if (!config || config._authRetried) return null
|
||||||
|
|
||||||
|
const hasOldAccessToken = Boolean(
|
||||||
|
getStoredAccessToken() || getRequestAuthorization(config),
|
||||||
|
)
|
||||||
|
if (!hasOldAccessToken) return null
|
||||||
|
|
||||||
|
config._authRetried = true
|
||||||
|
clearPersistedAuth()
|
||||||
|
const accessToken = await refreshPersistedAuth()
|
||||||
|
|
||||||
|
if (!accessToken) return null
|
||||||
|
|
||||||
|
config.headers = {
|
||||||
|
...(config.headers || {}),
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有旧 accessToken 过期时才尝试刷新并重放一次,未登录 401 直接去 Admin。
|
||||||
|
return request.request(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectAfterAuthExpired() {
|
||||||
|
clearPersistedAuth()
|
||||||
|
redirectToAdminLogin()
|
||||||
|
}
|
||||||
|
|
||||||
request.interceptors.response.use(
|
request.interceptors.response.use(
|
||||||
(response) => {
|
async (response) => {
|
||||||
const data = response.data as ApiResponse<any>
|
const data = response.data as ApiResponse<any>
|
||||||
|
|
||||||
if (response.status === 401 || data.code === 401) {
|
if (response.status === 401 || data.code === 401) {
|
||||||
clearPersistedAuth()
|
const retryResponse = await retryRequestWithFreshToken(
|
||||||
redirectToAdminLogin()
|
response.config as AuthRetryConfig,
|
||||||
return Promise.reject(new Error(data.msg || '登录已过期'))
|
)
|
||||||
|
if (retryResponse) return retryResponse
|
||||||
|
|
||||||
|
redirectAfterAuthExpired()
|
||||||
|
return Promise.reject(new Error(getAuthErrorMessage(data)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.code !== 200) {
|
if (data.code !== 200) {
|
||||||
@ -56,15 +112,24 @@ request.interceptors.response.use(
|
|||||||
|
|
||||||
return data.data as any
|
return data.data as any
|
||||||
},
|
},
|
||||||
(error) => {
|
async (error) => {
|
||||||
if (axios.isAxiosError<ApiResponse>(error)) {
|
if (axios.isAxiosError<ApiResponse>(error)) {
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
clearPersistedAuth()
|
const retryResponse = await retryRequestWithFreshToken(
|
||||||
redirectToAdminLogin()
|
error.config as AuthRetryConfig | undefined,
|
||||||
|
)
|
||||||
|
if (retryResponse) return retryResponse
|
||||||
|
|
||||||
|
redirectAfterAuthExpired()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Error(error.response?.data?.msg || error.message || '请求失败'),
|
new Error(
|
||||||
|
error.response?.data?.msg ||
|
||||||
|
error.response?.data?.message ||
|
||||||
|
error.message ||
|
||||||
|
'请求失败',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user