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

6.8 KiB
Raw Blame History

Session 完整性验证 - 修复说明

🐛 问题描述

现象:用户访问页面时出现错误:

Uncaught TypeError: Cannot read properties of undefined (reading 'nick_name')
at Home (home.tsx:315:33)

根本原因:Session 部分过期或数据丢失时,getUserSession 仍然返回 isAuthenticated: true,但 userInfoundefined

🔍 问题分析

问题场景

用户登录成功
  ↓
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 (修复前)

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

// 🔑 关键:验证 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

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

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 部分失效

# 模拟:手动清除 Cookie 中的部分数据(保留 isAuthenticated
# 预期结果:重定向到登录页

场景 2Session 完全有效

# 正常登录,访问 /home
# 预期结果:正常显示首页

场景 3Session 完全失效

# 清除所有 Cookie
# 预期结果:重定向到登录页

⚙️ Session 完整性检查规则

必须存在的数据

对于所有已认证用户:

  • userInfo - 用户信息对象
  • frontendJWT - 前端 JWT token

对于 OAuth 登录用户(非 admin):

  • accessToken - OAuth 访问令牌
  • refreshToken - OAuth 刷新令牌

对于 admin 用户:

  • userInfo - 用户信息对象
  • frontendJWT - 前端 JWT token
  • ⚠️ OAuth token 可选(使用模拟 token

验证逻辑

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 添加 isAuthenticateduserInfo 检查(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