Files
leaudit-platform-frontend/docs/Session完整性验证_修复说明.md
T
2025-12-05 00:09:32 +08:00

269 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Session 完整性验证 - 修复说明
## 🐛 问题描述
**现象**:用户访问页面时出现错误:
```
Uncaught TypeError: Cannot read properties of undefined (reading 'nick_name')
at Home (home.tsx:315:33)
```
**根本原因**:Session 部分过期或数据丢失时,`getUserSession` 仍然返回 `isAuthenticated: true`,但 `userInfo``undefined`
## 🔍 问题分析
### 问题场景
```
用户登录成功
Cookie Session 创建:
- isAuthenticated: true
- userInfo: { nick_name: "张三", ... }
- accessToken: "..."
- frontendJWT: "..."
经过一段时间(Session 部分失效)
Cookie Session 变为:
- isAuthenticated: true ✅ (仍然保留)
- userInfo: undefined ❌ (已丢失)
- accessToken: undefined ❌ (已丢失)
- frontendJWT: undefined ❌ (已丢失)
getUserSession 返回:
- isAuthenticated: true ❌ (错误!应该是 false)
- userInfo: undefined
home.tsx 访问 userInfo.nick_name
💥 TypeError: Cannot read properties of undefined
```
### 原有代码问题
**`app/api/login/auth.server.ts:340-349` (修复前)**
```typescript
return {
isAuthenticated: isAuthenticated && !isTokenExpired, // ❌ 只检查 token 过期
userRole,
accessToken,
refreshToken,
userInfo, // ❌ 可能是 undefined
isTokenExpired,
refreshedSession,
frontendJWT // ❌ 可能是 undefined
};
```
**问题**
1. 只检查 `isTokenExpired`(需要满足特定条件才会检查)
2. 不验证 `userInfo` 是否存在
3. 不验证 `frontendJWT` 是否存在
4. 不验证 OAuth token 信息是否完整
## ✅ 解决方案
### 1. 修复 `getUserSession` - 添加完整性验证
**位置**`app/api/login/auth.server.ts:340-374`
```typescript
// 🔑 关键:验证 session 完整性
// 如果 isAuthenticated 为 true,但缺少关键数据(userInfo),则认为 session 无效
let finalIsAuthenticated = isAuthenticated && !isTokenExpired;
if (finalIsAuthenticated) {
// 检查是否有用户信息
if (!userInfo) {
console.warn("⚠️ [getUserSession] Session 不完整:缺少 userInfo");
finalIsAuthenticated = false;
}
// 对于非 admin 用户,检查是否有必要的 OAuth token 信息
if (!isAdmin && (!accessToken || !refreshToken)) {
console.warn("⚠️ [getUserSession] Session 不完整:缺少 OAuth token 信息");
finalIsAuthenticated = false;
}
// 检查是否有前端 JWT
if (!frontendJWT) {
console.warn("⚠️ [getUserSession] Session 不完整:缺少 frontendJWT");
finalIsAuthenticated = false;
}
}
return {
isAuthenticated: finalIsAuthenticated, // ✅ 经过完整性验证
userRole,
accessToken,
refreshToken,
userInfo,
isTokenExpired,
refreshedSession,
frontendJWT
};
```
### 2. 增强 `home.tsx` loader - 服务端检查
**位置**`app/routes/home.tsx:48-85`
```typescript
export async function loader({ request }: LoaderFunctionArgs) {
try {
const { userRole, userInfo, frontendJWT, isAuthenticated } = await getUserSession(request);
// 🔑 检查用户是否已登录且有用户信息
if (!isAuthenticated || !userInfo) {
console.warn("⚠️ [Home Loader] 用户未登录或缺少用户信息,重定向到登录页");
const url = new URL(request.url);
return Response.redirect(`/login?redirect=${encodeURIComponent(url.pathname)}`, 302);
}
return Response.json({
homeData: { /* ... */ },
userRole,
userInfo,
frontendJWT
});
} catch (error) {
console.error('Failed to fetch dashboard data:', error);
return Response.json({ error: '获取数据失败,请稍后重试' }, { status: 500 });
}
}
```
### 3. 增强 Home 组件 - 客户端检查
**位置**`app/routes/home.tsx:111-118`
```typescript
export default function Home() {
const { userInfo, frontendJWT, /* ... */ } = useLoaderData<typeof loader>();
// 🔑 防御性检查:如果 userInfo 不存在,重定向到登录页
if (!userInfo) {
console.error("❌ [Home] userInfo 不存在,重定向到登录页");
if (typeof window !== 'undefined') {
window.location.href = '/login';
}
return null;
}
// ... 正常渲染
}
```
## 📊 完整数据流
### 修复后的流程
```
用户访问 /home
服务端 loader 执行
调用 getUserSession(request)
检查 session 完整性:
- isAuthenticated: true
- userInfo: undefined ❌
- frontendJWT: undefined ❌
完整性验证失败
返回 { isAuthenticated: false, userInfo: undefined, ... }
home.tsx loader 检查:
if (!isAuthenticated || !userInfo) { ... }
返回 302 重定向
浏览器跳转到 /login?redirect=/home
✅ 用户看到登录页,而不是错误页面
```
## 🧪 测试场景
### 场景 1Session 部分失效
```bash
# 模拟:手动清除 Cookie 中的部分数据(保留 isAuthenticated
# 预期结果:重定向到登录页
```
### 场景 2Session 完全有效
```bash
# 正常登录,访问 /home
# 预期结果:正常显示首页
```
### 场景 3Session 完全失效
```bash
# 清除所有 Cookie
# 预期结果:重定向到登录页
```
## ⚙️ Session 完整性检查规则
### 必须存在的数据
对于所有已认证用户:
-`userInfo` - 用户信息对象
-`frontendJWT` - 前端 JWT token
对于 OAuth 登录用户(非 admin):
-`accessToken` - OAuth 访问令牌
-`refreshToken` - OAuth 刷新令牌
对于 admin 用户:
-`userInfo` - 用户信息对象
-`frontendJWT` - 前端 JWT token
- ⚠️ OAuth token 可选(使用模拟 token
### 验证逻辑
```typescript
if (isAuthenticated) {
if (!userInfo) isAuthenticated = false
if (!frontendJWT) isAuthenticated = false
if (!isAdmin && !accessToken) isAuthenticated = false
if (!isAdmin && !refreshToken) isAuthenticated = false
}
```
## 🎯 修复收益
1. **用户体验改善**:Session 失效时看到登录页,而不是错误页面
2. **安全性增强**:防止部分 session 数据泄露导致的安全问题
3. **稳定性提升**:防止 `undefined` 访问导致的运行时错误
4. **调试友好**:清晰的日志输出,便于定位问题
## 📁 修改的文件
1. **`app/api/login/auth.server.ts`**
- 添加 session 完整性验证逻辑(line 340-374
2. **`app/routes/home.tsx`**
- Loader 添加 `isAuthenticated``userInfo` 检查(line 54-58
- 组件添加客户端防御性检查(line 112-118
3. **`docs/Session完整性验证_修复说明.md`**(新增)
- 完整的问题分析和修复文档
## 🔗 相关文档
- `docs/服务端Token过期处理_实现说明.md` - Token 过期处理机制
- `docs/统一登录流程_实施记录.md` - 登录流程文档
- `docs/Token自动管理_测试说明.md` - Token 管理架构
---
**修复时间**: 2025-01-18
**修复者**: Claude Code