You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.0 KiB
6.0 KiB
认证系统重构:集中式状态管理与跨标签页同步
日期: 2026-02-06 类型: 重构 (Refactor) 影响范围: 前端认证流程 Breaking Changes: 无
概述
重构认证系统,引入集中式 AuthContext 管理认证状态,解决以下问题:
- 403/401 错误处理不完整(未清除 React Query 缓存)
- 跨标签页登录/退出状态不同步
- 潜在的登录页面重定向循环
- 认证状态管理分散(localStorage、Zustand、React Query)
问题诊断
1. 403/401 处理不完整
- 位置: client.ts:107-127
- 问题: 仅清除 localStorage + Zustand,未清除 React Query 缓存
- 影响: 过期数据残留,可能导致状态不一致
2. 跨标签页状态不同步
- 问题: 无
storage事件监听 - 影响:
- 标签页 A 登录 → 标签页 B 无感知
- 标签页 A 退出 → 标签页 B 仍显示已登录
3. 潜在的重定向循环
- 位置: client.ts:111
- 问题: 使用
window.location.href = '/login'硬跳转 - 影响: 若 LoginPage 触发 403 请求 → 再次跳转 /login → 死循环
4. ProtectedRoute 逻辑复杂
- 位置: Router.tsx:34-85
- 问题: 3 个 useEffect,可能产生竞态条件
- 影响: 维护困难,逻辑难以追踪
5. 退出登录不彻底
- 位置: useAuth.ts:32-44
- 问题: 仅清除 React Query,未清除 localStorage token 和 Zustand user
- 影响: 退出后状态残留
解决方案
架构设计
AuthContext (新增)
├── 统一管理 token/user 状态
├── 监听 storage 事件(跨标签页同步)
├── 提供 login/logout/clearAuth 方法
├── 防重入保护(避免循环跳转)
└── 暴露 useAuth hook
AuthManager (新增)
└── 全局 clearAuth 注册器(供 axios 拦截器使用)
client.ts 拦截器
└── 调用 AuthManager.clearAuth()(清除所有状态)
ProtectedRoute
└── 简化为单一职责:检查 token + 显示加载状态
变更文件
新增文件
-
client/src/contexts/AuthContext.tsx (145 行)
- 集中式认证状态管理
- 跨标签页同步逻辑
- 防重入保护
-
client/src/lib/auth-manager.ts (30 行)
- 全局 clearAuth 注册器
- 供非 React 上下文使用(如 axios 拦截器)
修改文件
-
client/src/services/api/client.ts
- 移除直接操作 localStorage 和 Zustand
- 使用
clearAuth()统一清除认证状态 - 添加延迟跳转,确保状态清除完成
-
- ProtectedRoute 从 50 行简化到 35 行
- 移除 3 个 useEffect,合并为单一逻辑
- 使用
useAuthhook 获取认证状态
-
client/src/pages/LoginPage.tsx
- 使用
authLogin()替代手动保存 token + setUser - 自动触发跨标签页同步
- 使用
-
client/src/hooks/api/useAuth.ts
- useLogout 使用 AuthContext 统一清除逻辑
- 确保退出登录彻底清除所有状态
-
client/src/app/providers/index.tsx
- 集成 AuthProvider 到应用 Provider 树
- 位于 QueryProvider 内部(需要 QueryClient)
核心功能
1. 集中式认证管理
// 登录
authLogin(token, user); // 自动保存到 localStorage + Zustand + 触发 storage 事件
// 退出
logout(); // 清除 localStorage + Zustand + React Query + 触发 storage 事件
// 清除认证(403/401)
clearAuth(reason); // 带防重入保护
2. 跨标签页同步
// 监听 storage 事件
window.addEventListener('storage', (e) => {
if (e.key === 'token') {
if (e.newValue) {
// 其他标签页登录 → 刷新当前页面
window.location.reload();
} else {
// 其他标签页退出 → 清除本地状态 + 跳转登录
clearAuth();
window.location.href = '/login';
}
}
});
3. 防重入保护
const clearingRef = useRef(false);
function clearAuth(reason) {
if (clearingRef.current) {
console.log('⚠️ 正在清除中,跳过重复调用');
return;
}
clearingRef.current = true;
// ... 清除逻辑
setTimeout(() => {
clearingRef.current = false;
}, 100);
}
测试验证
手动测试清单
- 登录成功后,token 和 user 正确保存
- 刷新页面后,认证状态保持
- 访问受保护路由时,未登录自动跳转登录页
- 退出登录后,所有状态清除(localStorage、Zustand、React Query)
- 403/401 错误触发时,自动清除状态并跳转登录页
- 跨标签页测试:
- 标签页 A 登录 → 标签页 B 自动刷新并登录
- 标签页 A 退出 → 标签页 B 自动跳转登录页
- 无重定向循环(登录页不会无限跳转)
性能影响
- 代码体积: +175 行(新增文件),-30 行(简化逻辑)
- 运行时开销:
- 新增 1 个 storage 事件监听器(轻量)
- 简化 ProtectedRoute 逻辑,减少 useEffect 数量
- 内存占用: 无显著影响
向后兼容性
✅ 完全兼容
- 保持现有 API 接口不变
- usePhoneLogin、useLogout 等 hooks 签名不变
- 组件使用方式不变
后续优化建议
- Token 刷新机制: 添加 token 过期前自动刷新
- 离线检测: 网络断开时暂停 API 请求
- 会话超时: 长时间无操作自动退出
- 安全增强: 添加 CSRF token 保护
相关文档
作者
- Claude Sonnet 4.5
- 协作者:chisdy