fix: deduplicate expired token handling
Build and Deploy Vue3 / build (push) Successful in 1m36s
Build and Deploy Vue3 / deploy (push) Successful in 53s

This commit is contained in:
shiran
2026-08-05 09:02:39 +08:00
parent 67af0c66fd
commit 66f974a71b
2 changed files with 37 additions and 24 deletions
+9 -1
View File
@@ -1,4 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router'
import { TOKEN_KEY, TOKEN_EXPIRE_KEY, USER_INFO_KEY } from '@/config/env.js'
import AdminLayout from '../components/layout/AdminLayout.vue'
import OperationLog from '@/views/system/OperationLog.vue'
@@ -809,7 +810,14 @@ router.beforeEach((to, from, next) => {
document.title = to.meta.title ? `${to.meta.title} - 007UI管理系统` : '007UI管理系统'
// 这里可以添加登录验证逻辑
const isAuthenticated = localStorage.getItem('token')
const token = localStorage.getItem(TOKEN_KEY)
const expire = Number(localStorage.getItem(TOKEN_EXPIRE_KEY))
const isAuthenticated = !!token && Number.isFinite(expire) && expire > 0 && Date.now() < expire * 1000
if (token && !isAuthenticated) {
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
}
if (to.path !== '/login' && !isAuthenticated) {
next({ path: '/login' })
} else {
+28 -23
View File
@@ -46,6 +46,22 @@ const isTokenExpiringSoon = () => {
let isRefreshing = false
// 等待刷新token的请求队列
let refreshSubscribers = []
let authRedirecting = false
const clearAuthAndRedirect = (message = '登录过期,请重新登录') => {
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
onTokenRefreshed(null)
if (authRedirecting) return
authRedirecting = true
ElMessage.closeAll()
ElMessage.warning(message)
if (router.currentRoute.value.path !== '/login') router.replace('/login')
}
// 鉴权失效后终止原页面请求链,避免并发请求进入各页面 catch 后重复弹错。
const stopExpiredRequest = () => new Promise(() => {})
// 添加请求到队列
const subscribeTokenRefresh = (callback) => {
@@ -83,20 +99,12 @@ const doRefreshToken = async () => {
}
}
// 刷新失败,触发登出逻辑
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
router.push('/login')
clearAuthAndRedirect()
return null
} catch (error) {
console.error('Token刷新失败:', error)
// 刷新失败,触发登出逻辑
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
router.push('/login')
clearAuthAndRedirect()
return null
}
}
@@ -122,6 +130,10 @@ class Request {
return config
},
(error) => {
if (error.response?.status === 401) {
clearAuthAndRedirect()
return stopExpiredRequest()
}
return Promise.reject(error)
}
)
@@ -206,14 +218,8 @@ http2.interceptors.request.use(async config => {
if (urlNeedAuth(config.url)) {
// 检查token是否已过期
if (isTokenExpired()) {
if (token) {
localStorage.removeItem(TOKEN_KEY)
localStorage.removeItem(TOKEN_EXPIRE_KEY)
localStorage.removeItem(USER_INFO_KEY)
ElMessage.warning('登录过期,请重新登录')
}
router.push('/login')
return Promise.reject(new Error('Token已过期'))
clearAuthAndRedirect(token ? '登录过期,请重新登录' : '请先登录')
return stopExpiredRequest()
}
// 检查token是否即将过期,进行无感刷新
@@ -251,6 +257,7 @@ http2.interceptors.request.use(async config => {
})
} else {
// 正常情况,直接使用token
authRedirecting = false
config.headers.Authorization = `Bearer ${token}`
}
}
@@ -269,14 +276,12 @@ http2.interceptors.response.use(
}
const { status } = error.response;
if (status === 401) {
localStorage.removeItem(TOKEN_KEY);
ElMessage.warning('登陆过期,请重新登陆')
router.push('/login')
return Promise.reject();
clearAuthAndRedirect()
return stopExpiredRequest()
}
// 其他错误处理(可选)
return Promise.reject(error);
}
);
export default request
export default request