diff --git a/src/router/index.js b/src/router/index.js index 656d932..1ba0a81 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -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 { diff --git a/src/utils/request.js b/src/utils/request.js index 2ea6943..67c44f2 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -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 \ No newline at end of file +export default request diff --git a/src/views/user-vm/UserVmDetail.vue b/src/views/user-vm/UserVmDetail.vue index 58689e9..2f09ec0 100644 --- a/src/views/user-vm/UserVmDetail.vue +++ b/src/views/user-vm/UserVmDetail.vue @@ -889,6 +889,10 @@ + + +
迁移完成后,用户商品到期时间向后顺延;0 表示不调整
+