fix(auth):修复身份认证失效后跳回登录页面又回到系统内的问题
This commit is contained in:
1 parent
336ed835f1
commit
6aed01fba8
6 files changed
+135
-35
No files matched your search
@@ -27,6 +27,9 @@ const EXPIRES_AT_KEY = 'admin_token_expires_at'
|
||||
const REMEMBER_ME_KEY = 'admin_remember_me'
|
||||
const TOKEN_BUFFER_SECONDS = 60
|
||||
|
||||
// 并发 401 时只清理并跳转一次;登录/刷新成功后由 setTokens 复位
|
||||
let isLoggingOut = false
|
||||
|
||||
// Dynamic storage selection based on "remember me" preference
|
||||
function getStorage(): Storage {
|
||||
// Check user's "remember me" preference (defaults to true for backward compatibility)
|
||||
@@ -45,6 +48,8 @@ export function getRememberMe(): boolean {
|
||||
}
|
||||
|
||||
export function setTokens(tokens: TokenPair): void {
|
||||
// 登录/刷新成功,复位登出节流标志,保证下次失效仍可跳转登录页
|
||||
isLoggingOut = false
|
||||
const storage = getStorage()
|
||||
storage.setItem(ACCESS_TOKEN_KEY, tokens.accessToken)
|
||||
storage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken)
|
||||
@@ -94,6 +99,38 @@ export function onTokenRefreshed(cb: (tokens: TokenPair) => void): void {
|
||||
tokenRefreshedCallback = cb
|
||||
}
|
||||
|
||||
// 认证彻底失效时由 auth store 注册的清理回调(清空响应式状态与持久化登录数据)
|
||||
let authExpiredCallback: (() => void) | null = null
|
||||
|
||||
export function onAuthExpired(cb: () => void): void {
|
||||
authExpiredCallback = cb
|
||||
}
|
||||
|
||||
/** 取登录失效后应回跳的路径(排除登录页自身) */
|
||||
function getLoginRedirectPath(): string {
|
||||
const hash = window.location.hash || ''
|
||||
const path = hash.startsWith('#') ? hash.slice(1) : hash
|
||||
const clean = path.split('?')[0]
|
||||
if (!clean || clean.startsWith('/admin/login')) return '/admin'
|
||||
return clean
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证彻底失效(401 且无法刷新):先清空全部登录数据,再跳转登录页。
|
||||
* 并发 401 只执行一次清理与跳转,避免重复弹窗。
|
||||
*/
|
||||
function handleAuthExpired(): void {
|
||||
if (isLoggingOut) return
|
||||
isLoggingOut = true
|
||||
rejectPending(new Error('authentication expired'))
|
||||
// 由 auth store 清空响应式状态与持久化数据(store 自身负责全部清理)
|
||||
authExpiredCallback?.()
|
||||
// 兜底清除 token storage
|
||||
clearTokens()
|
||||
Message.warning('登录状态已失效,请重新登录')
|
||||
window.location.hash = `#/admin/login?redirect=${encodeURIComponent(getLoginRedirectPath())}`
|
||||
}
|
||||
|
||||
function replayPending(token: string): void {
|
||||
pendingRequests.forEach(({ resolve }) => resolve(token))
|
||||
pendingRequests = []
|
||||
@@ -151,10 +188,7 @@ request.interceptors.request.use(
|
||||
const newTokens = await doRefresh()
|
||||
replayPending(newTokens.accessToken)
|
||||
} catch (error) {
|
||||
rejectPending(error)
|
||||
clearTokens()
|
||||
showRequestError(error, config)
|
||||
window.location.hash = '#/admin/login'
|
||||
handleAuthExpired()
|
||||
return Promise.reject(error)
|
||||
} finally {
|
||||
isRefreshing = false
|
||||
@@ -263,10 +297,7 @@ request.interceptors.response.use(
|
||||
originalRequest.headers.Authorization = `Bearer ${newTokens.accessToken}`
|
||||
return request(originalRequest)
|
||||
} catch (refreshError) {
|
||||
rejectPending(refreshError)
|
||||
clearTokens()
|
||||
showRequestError(refreshError, originalRequest)
|
||||
window.location.hash = '#/admin/login'
|
||||
handleAuthExpired()
|
||||
return Promise.reject(refreshError)
|
||||
} finally {
|
||||
isRefreshing = false
|
||||
|
||||
Reference in new issue
Block a user