# OAuth 单点登录测试指南 ## 概述 OAuth 2.0 单点登录已成功集成到标注平台!现在可以使用 SSO 认证中心进行登录。 ## 配置信息 ### 后端配置 (`backend/config.yaml`) ```yaml oauth: enabled: true base_url: "http://192.168.92.61:8000" client_id: "sRyfcQwNVoFimigzuuZxhqd36fPkVN5G" client_secret: "96RuKb4obAn9bQ9i5NtINiKBMvF_9uuCR7eNzD9dWQMbOWZaV3P593-8yLOqzWRd" redirect_uri: "http://localhost:4200/auth/callback" scope: "profile email" ``` ### OAuth 端点 - **授权端点**: `http://192.168.92.61:8000/oauth/authorize` - **令牌端点**: `http://192.168.92.61:8000/oauth/token` - **用户信息端点**: `http://192.168.92.61:8000/oauth/userinfo` ## 测试步骤 ### 1. 启动服务 **后端**: ```bash cd backend python main.py ``` **前端**: ```bash cd web yarn nx serve lq_label ``` ### 2. 访问登录页面 打开浏览器访问: `http://localhost:4200/login` ### 3. 使用 OAuth 登录 1. 点击 **"使用 SSO 登录"** 按钮 2. 浏览器会重定向到 OAuth 认证中心 (`http://192.168.92.61:8000`) 3. 在 SSO 登录页面输入用户名和密码 4. 授权后,浏览器会重定向回标注平台 (`http://localhost:4200/auth/callback`) 5. 系统自动完成登录,跳转到首页 ### 4. 验证登录状态 登录成功后,你应该能看到: - 右上角显示用户名和头像 - 可以访问所有受保护的页面(项目、任务、标注等) - 用户菜单显示用户信息和"退出登录"按钮 ## OAuth 登录流程 ``` 1. 用户点击"使用 SSO 登录" ↓ 2. 前端调用 /api/oauth/login 获取授权 URL 和 state ↓ 3. 前端保存 state 到 sessionStorage ↓ 4. 前端重定向到 OAuth 授权页面 ↓ 5. 用户在 SSO 登录并授权 ↓ 6. OAuth 重定向回 /auth/callback?code=xxx&state=yyy ↓ 7. 前端验证 state 参数 ↓ 8. 前端调用 /api/oauth/callback 用 code 换取 token ↓ 9. 后端用 code 向 OAuth 换取 access_token ↓ 10. 后端用 access_token 获取用户信息 ↓ 11. 后端同步用户到本地数据库 ↓ 12. 后端生成本地 JWT tokens ↓ 13. 前端保存 tokens 和用户信息 ↓ 14. 前端跳转到首页 ``` ## API 端点 ### 后端 OAuth API | 端点 | 方法 | 描述 | |------|------|------| | `/api/oauth/login` | GET | 获取授权 URL 和 state | | `/api/oauth/callback` | GET | 处理 OAuth 回调,换取 token | | `/api/oauth/status` | GET | 获取 OAuth 配置状态 | ### 示例请求 **获取授权 URL**: ```bash curl http://localhost:8000/api/oauth/login ``` **响应**: ```json { "authorization_url": "http://192.168.92.61:8000/oauth/authorize?response_type=code&client_id=sRyfcQwNVoFimigzuuZxhqd36fPkVN5G&redirect_uri=http://localhost:4200/auth/callback&scope=profile+email&state=xxx", "state": "xxx" } ``` **处理回调**: ```bash curl "http://localhost:8000/api/oauth/callback?code=xxx&state=yyy" ``` **响应**: ```json { "access_token": "eyJ...", "refresh_token": "eyJ...", "token_type": "bearer", "user": { "id": "user_xxx", "username": "testuser", "email": "test@example.com", "role": "annotator", "created_at": "2024-01-22T..." } } ``` ## 用户同步逻辑 ### 首次登录 1. OAuth 返回用户信息(包含 `sub` 或 `id` 字段) 2. 系统检查本地数据库是否存在该 OAuth 用户 3. 如果不存在,创建新用户记录: - `oauth_provider`: "sso" - `oauth_id`: OAuth 用户 ID - `username`: OAuth 用户名 - `email`: OAuth 邮箱 - `role`: "annotator"(默认) - `password_hash`: ""(OAuth 用户不需要密码) ### 再次登录 1. 系统根据 `oauth_provider` 和 `oauth_id` 查找用户 2. 更新用户名和邮箱(如果有变化) 3. 返回现有用户信息 ## 角色管理 **当前实现**: - 所有 OAuth 用户默认角色为 `annotator`(标注员) - SSO 暂时未提供角色信息 **未来扩展**: - 当 SSO 提供角色信息后,可以从 OAuth 用户信息中读取角色 - 支持 `admin` 和 `annotator` 两种角色 - 管理员可以删除项目和任务 ## 安全特性 ### State 参数验证 - 前端生成随机 state 参数并保存到 sessionStorage - OAuth 回调时验证 state 参数是否匹配 - 防止 CSRF 攻击 ### Token 安全 - OAuth access_token 仅用于获取用户信息 - 系统生成独立的 JWT tokens 用于后续 API 调用 - JWT tokens 有过期时间(access: 15分钟,refresh: 7天) ### 用户隔离 - 每个 OAuth 用户在本地数据库有独立记录 - 用户只能访问自己的标注数据 - 管理员可以访问所有数据 ## 故障排除 ### 问题 1: "无效的客户端ID" 错误 **原因**: OAuth 服务器上的应用 ID 配置不正确 **解决方案**: - 确认 `backend/config.yaml` 中的 `client_id` 是正确的 - 当前正确的 ID:`sRyfcQwNVoFimigzuuZxhqd36fPkVN5G` - 确认 OAuth 服务器上已创建该应用 ### 问题 2: "无效的重定向URI" 错误 **原因**: 回调 URL 未在 OAuth 服务器上配置 **解决方案**: - 在 OAuth 服务器上添加回调 URL:`http://localhost:4200/auth/callback` - 确保 URL 完全匹配(包括协议、端口、路径) ### 问题 3: "授权码已被使用" 错误 **原因**: React StrictMode 在开发环境下会导致组件渲染两次,授权码被使用两次 **解决方案**: - 已在 `oauth-callback.tsx` 中添加防重复调用逻辑(使用 `isProcessing` 状态) - 生产环境不会有此问题(StrictMode 仅在开发环境启用) ### 问题 4: Client ID 和 Secret 配置错误 **症状**: 配置文件中 `client_id` 和 `client_secret` 的值搞反了 **正确配置**: ```yaml oauth: client_id: "sRyfcQwNVoFimigzuuZxhqd36fPkVN5G" client_secret: "96RuKb4obAn9bQ9i5NtINiKBMvF_9uuCR7eNzD9dWQMbOWZaV3P593-8yLOqzWRd" ``` ### 问题 5: 点击"使用 SSO 登录"没有反应 **检查**: 1. 打开浏览器控制台查看错误 2. 确认后端 OAuth 配置正确 3. 确认 OAuth 服务可访问 ### 问题 6: 重定向到 OAuth 后无法返回 **检查**: 1. 确认 `redirect_uri` 配置正确 2. 确认 OAuth 中心配置了正确的回调 URL 3. 检查浏览器控制台错误 ### 问题 7: 回调后显示"登录失败" **检查**: 1. 查看浏览器控制台错误信息 2. 查看后端日志 3. 确认 OAuth 令牌交换成功 4. 确认用户信息获取成功 ### 问题 8: State 参数不匹配 **原因**: sessionStorage 被清除或浏览器不支持 **解决方案**: 1. 不要在登录过程中清除浏览器缓存 2. 确保浏览器支持 sessionStorage 3. 检查是否有浏览器扩展干扰 ## 测试账号 请使用 SSO 认证中心提供的测试账号进行测试。 ## 开发调试 ### 查看 OAuth 配置状态 ```bash curl http://localhost:8000/api/oauth/status ``` ### 查看用户数据库 ```bash cd backend sqlite3 annotation_platform.db SELECT * FROM users WHERE oauth_provider = 'sso'; ``` ### 清除 OAuth 用户 ```bash cd backend sqlite3 annotation_platform.db DELETE FROM users WHERE oauth_provider = 'sso'; ``` ## 生产环境配置 ### 更新回调 URL 修改 `backend/config.yaml`: ```yaml oauth: redirect_uri: "http://your-production-domain.com/auth/callback" ``` ### 更新前端 CORS 修改 `backend/main.py`: ```python app.add_middleware( CORSMiddleware, allow_origins=[ "http://your-production-domain.com", ], ... ) ``` ## 总结 OAuth 单点登录已成功集成!主要特性: - ✅ 支持 OAuth 2.0 授权码模式 - ✅ 自动用户同步和创建 - ✅ State 参数防 CSRF 攻击 - ✅ 独立的 JWT token 管理 - ✅ 用户角色支持(默认 annotator) - ✅ 与现有认证系统兼容 - ✅ 支持传统用户名密码登录 现在可以使用 SSO 登录标注平台了!🎉 --- **实现日期**: 2024-01-22 **状态**: ✅ 完成 **文档版本**: 1.0