之前前端的认证管理存在以下问题:
- 401 错误处理不完整:只在响应拦截器的错误处理中处理 HTTP 401,但如果后端返回
{success: false, code: 401}的业务错误(HTTP 200),不会触发跳转登录页 - 业务错误码未检查认证失败:
handleBusinessError函数没有检查认证相关的业务错误码(401, 40101, 40102, 40103) - Token 刷新可能失败但没有统一处理:某些接口可能返回 401 但没有正确跳转登录页
- 缺少 Token 自动刷新机制:Token 过期前没有自动刷新,导致用户操作时突然失效
文件: frontend/src/api/request.ts
优化内容:
- 在成功响应中检查业务错误码(401, 40101, 40102, 40103)
- 优先处理认证错误,不依赖
skipErrorHandler配置 - 统一跳转登录页逻辑
// 响应拦截器 - 成功响应处理
instance.interceptors.response.use(
(response: AxiosResponse) => {
// 检查业务状态码
const data = response.data as ApiResponse
if (data && typeof data === 'object' && 'success' in data) {
if (!data.success) {
// 检查是否是认证错误(优先处理,不依赖 skipErrorHandler)
const code = data.code
if (code === 401 || code === 40101 || code === 40102 || code === 40103) {
console.log('🔒 业务错误:认证失败 (HTTP 200),跳转登录页')
authStore.clearAuthInfo()
router.push('/login')
ElMessage.error(data.message || '登录已过期,请重新登录')
return Promise.reject(new Error(data.message || '认证失败'))
}
// 其他业务错误
if (!config.skipErrorHandler) {
handleBusinessError(data)
return Promise.reject(new Error(data.message || '请求失败'))
}
}
}
return response.data
},
// ... 错误响应处理
)文件: frontend/src/api/request.ts
优化内容:
- 在
handleBusinessError函数中添加认证错误码处理 - 统一认证错误的处理逻辑
const handleBusinessError = (data: ApiResponse) => {
const { code, message } = data
const authStore = useAuthStore()
switch (code) {
case 401:
case 40101: // 未授权
case 40102: // Token 无效
case 40103: // Token 过期
console.log('🔒 业务错误:认证失败,跳转登录页')
authStore.clearAuthInfo()
router.push('/login')
ElMessage.error(message || '登录已过期,请重新登录')
break
// ... 其他错误码处理
}
}文件: frontend/src/main.ts
优化内容:
- 在全局错误处理器中捕获未处理的认证错误
- 确保所有认证错误都能跳转到登录页
app.config.errorHandler = (err, vm, info) => {
console.error('全局错误:', err, info)
// 检查是否是认证错误
if (err && typeof err === 'object') {
const error = err as any
if (
error.message?.includes('认证失败') ||
error.message?.includes('登录已过期') ||
error.message?.includes('Token') ||
error.response?.status === 401 ||
error.code === 401
) {
console.log('🔒 全局错误处理:检测到认证错误,跳转登录页')
const authStore = useAuthStore()
authStore.clearAuthInfo()
router.push('/login')
}
}
}文件: frontend/src/utils/auth.ts
新增功能:
isTokenValid(): 检查 token 是否有效parseToken(): 解析 token 获取 payloadgetTokenRemainingTime(): 获取 token 剩余有效时间isTokenExpiringSoon(): 检查 token 是否即将过期(默认 5 分钟)autoRefreshToken(): 自动刷新即将过期的 tokensetupTokenRefreshTimer(): 设置定时器,每分钟检查并刷新 token
使用方式:
// 在应用初始化时启动定时器
import { setupTokenRefreshTimer } from '@/utils/auth'
// 用户登录成功后
if (authStore.isAuthenticated) {
setupTokenRefreshTimer()
}文件: frontend/src/utils/auth.ts
新增功能:
isAuthError(): 检查是否是认证错误handleAuthError(): 统一处理认证错误- Token 相关工具函数
使用示例:
import { isAuthError, handleAuthError } from '@/utils/auth'
try {
const response = await api.getData()
} catch (error) {
if (isAuthError(error)) {
handleAuthError(error)
} else {
// 处理其他错误
}
}401: 未授权(Unauthorized)
401: 认证失败(通用)40101: 未授权(未登录)40102: Token 无效40103: Token 过期
用户输入账号密码
↓
调用登录 API
↓
后端验证成功,返回 access_token 和 refresh_token
↓
前端保存 token 到 localStorage 和 Pinia store
↓
设置 axios 请求头 Authorization
↓
启动 token 自动刷新定时器
↓
跳转到目标页面
定时器每分钟检查 token
↓
检查 token 是否即将过期(< 5 分钟)
↓
如果即将过期,调用刷新 API
↓
使用 refresh_token 获取新的 access_token
↓
更新 localStorage 和 Pinia store
↓
更新 axios 请求头
API 请求返回 401 或认证错误码
↓
响应拦截器捕获错误
↓
检查是否是 refresh 请求本身失败
↓
如果不是,尝试刷新 token
↓
如果刷新成功,重试原请求
↓
如果刷新失败,清除认证信息
↓
跳转到登录页
↓
显示错误提示
场景: 用户长时间未操作,token 过期
预期行为:
- 用户操作时,API 返回 401
- 前端自动尝试刷新 token
- 如果刷新成功,继续原操作
- 如果刷新失败,跳转到登录页
场景: refresh_token 也过期了
预期行为:
- 用户操作时,API 返回 401
- 前端尝试刷新 token,但 refresh_token 也无效
- 清除认证信息
- 跳转到登录页
- 显示"登录已过期,请重新登录"
场景: 后端返回 HTTP 200,但 {success: false, code: 401}
预期行为:
- 响应拦截器检测到业务错误码 401
- 清除认证信息
- 跳转到登录页
- 显示错误消息
场景: Token 即将过期(剩余 < 5 分钟)
预期行为:
- 定时器检测到 token 即将过期
- 自动调用刷新 API
- 更新 token
- 用户无感知,继续操作
- 避免无限循环: 如果 refresh 请求本身返回 401,不要再次尝试刷新
- 网络错误处理: 网络错误或服务器错误(5xx)不要清除认证信息
- 并发请求: 多个请求同时返回 401 时,只刷新一次 token
- 路由守卫: 确保路由守卫和 API 拦截器的认证逻辑一致
- Token 存储: 同时使用 localStorage 和 Pinia store,确保刷新页面后认证状态不丢失
frontend/src/api/request.ts: API 请求拦截器和响应拦截器frontend/src/stores/auth.ts: 认证状态管理frontend/src/router/index.ts: 路由守卫frontend/src/main.ts: 应用初始化和全局错误处理frontend/src/utils/auth.ts: 认证工具函数frontend/src/views/Auth/Login.vue: 登录页面
- Token 刷新队列: 实现请求队列,避免并发刷新
- 离线模式: 支持离线缓存,网络恢复后自动同步
- 多标签页同步: 使用 BroadcastChannel 或 localStorage 事件同步多标签页的认证状态
- 安全增强: 实现 CSRF 保护、XSS 防护等安全措施
- 监控和日志: 集成前端监控服务,记录认证相关的错误和异常