# 认证系统重构:集中式状态管理与跨标签页同步 **日期**: 2026-02-06 **类型**: 重构 (Refactor) **影响范围**: 前端认证流程 **Breaking Changes**: 无 ## 概述 重构认证系统,引入集中式 `AuthContext` 管理认证状态,解决以下问题: 1. 403/401 错误处理不完整(未清除 React Query 缓存) 2. 跨标签页登录/退出状态不同步 3. 潜在的登录页面重定向循环 4. 认证状态管理分散(localStorage、Zustand、React Query) ## 问题诊断 ### 1. 403/401 处理不完整 - **位置**: [client.ts:107-127](../../client/src/services/api/client.ts#L107-L127) - **问题**: 仅清除 localStorage + Zustand,未清除 React Query 缓存 - **影响**: 过期数据残留,可能导致状态不一致 ### 2. 跨标签页状态不同步 - **问题**: 无 `storage` 事件监听 - **影响**: - 标签页 A 登录 → 标签页 B 无感知 - 标签页 A 退出 → 标签页 B 仍显示已登录 ### 3. 潜在的重定向循环 - **位置**: [client.ts:111](../../client/src/services/api/client.ts#L111) - **问题**: 使用 `window.location.href = '/login'` 硬跳转 - **影响**: 若 LoginPage 触发 403 请求 → 再次跳转 /login → 死循环 ### 4. ProtectedRoute 逻辑复杂 - **位置**: [Router.tsx:34-85](../../client/src/app/Router.tsx#L34-L85) - **问题**: 3 个 useEffect,可能产生竞态条件 - **影响**: 维护困难,逻辑难以追踪 ### 5. 退出登录不彻底 - **位置**: [useAuth.ts:32-44](../../client/src/hooks/api/useAuth.ts#L32-L44) - **问题**: 仅清除 React Query,未清除 localStorage token 和 Zustand user - **影响**: 退出后状态残留 ## 解决方案 ### 架构设计 ``` AuthContext (新增) ├── 统一管理 token/user 状态 ├── 监听 storage 事件(跨标签页同步) ├── 提供 login/logout/clearAuth 方法 ├── 防重入保护(避免循环跳转) └── 暴露 useAuth hook AuthManager (新增) └── 全局 clearAuth 注册器(供 axios 拦截器使用) client.ts 拦截器 └── 调用 AuthManager.clearAuth()(清除所有状态) ProtectedRoute └── 简化为单一职责:检查 token + 显示加载状态 ``` ## 变更文件 ### 新增文件 1. **[client/src/contexts/AuthContext.tsx](../../client/src/contexts/AuthContext.tsx)** (145 行) - 集中式认证状态管理 - 跨标签页同步逻辑 - 防重入保护 2. **[client/src/lib/auth-manager.ts](../../client/src/lib/auth-manager.ts)** (30 行) - 全局 clearAuth 注册器 - 供非 React 上下文使用(如 axios 拦截器) ### 修改文件 1. **[client/src/services/api/client.ts](../../client/src/services/api/client.ts)** - 移除直接操作 localStorage 和 Zustand - 使用 `clearAuth()` 统一清除认证状态 - 添加延迟跳转,确保状态清除完成 2. **[client/src/app/Router.tsx](../../client/src/app/Router.tsx)** - ProtectedRoute 从 50 行简化到 35 行 - 移除 3 个 useEffect,合并为单一逻辑 - 使用 `useAuth` hook 获取认证状态 3. **[client/src/pages/LoginPage.tsx](../../client/src/pages/LoginPage.tsx)** - 使用 `authLogin()` 替代手动保存 token + setUser - 自动触发跨标签页同步 4. **[client/src/hooks/api/useAuth.ts](../../client/src/hooks/api/useAuth.ts)** - useLogout 使用 AuthContext 统一清除逻辑 - 确保退出登录彻底清除所有状态 5. **[client/src/app/providers/index.tsx](../../client/src/app/providers/index.tsx)** - 集成 AuthProvider 到应用 Provider 树 - 位于 QueryProvider 内部(需要 QueryClient) ## 核心功能 ### 1. 集中式认证管理 ```typescript // 登录 authLogin(token, user); // 自动保存到 localStorage + Zustand + 触发 storage 事件 // 退出 logout(); // 清除 localStorage + Zustand + React Query + 触发 storage 事件 // 清除认证(403/401) clearAuth(reason); // 带防重入保护 ``` ### 2. 跨标签页同步 ```typescript // 监听 storage 事件 window.addEventListener('storage', (e) => { if (e.key === 'token') { if (e.newValue) { // 其他标签页登录 → 刷新当前页面 window.location.reload(); } else { // 其他标签页退出 → 清除本地状态 + 跳转登录 clearAuth(); window.location.href = '/login'; } } }); ``` ### 3. 防重入保护 ```typescript 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 签名不变 - 组件使用方式不变 ## 后续优化建议 1. **Token 刷新机制**: 添加 token 过期前自动刷新 2. **离线检测**: 网络断开时暂停 API 请求 3. **会话超时**: 长时间无操作自动退出 4. **安全增强**: 添加 CSRF token 保护 ## 相关文档 - [认证流程架构图](../guides/authentication-flow.md)(待创建) - [跨标签页同步原理](../guides/cross-tab-sync.md)(待创建) ## 作者 - Claude Sonnet 4.5 - 协作者:chisdy