# 技术方案-REQ-认证-统一认证与登录 > **版本**: v1.0 | **日期**: 2026-08-05 | **状态**: 预研整合(与 PRD v1.1 对齐) > **作者**: 宋献(产品)/ 技术评审:— > **关联文档**: > - PRD:`../01-产品文档/01-认证与登录/PRD-REQ-认证-统一认证与登录-v1.1.md` > - 测试用例:`../03-测试文档/03-功能测试用例/TC-REQ-认证-统一认证与登录-v1.0.md` > - OTP 运维:`部署运维/06-OTP二次验证实现.md` > - 扫码+OTP 部署:`部署运维/07-扫码登录OTP部署指南-v0.7.0.md` > - 管理端登录序列:`技术架构/05-架构图/admin-login-sequence.mermaid` --- ## 1. 背景与目标 本技术方案是认证与登录模块的**技术单一真源**,与 PRD v1.1、测试用例 v1.0 形成「三件套」。 目标: 1. 收敛散落在运维/测试/架构文档中的认证技术设计,统一到 `02-技术文档`; 2. 明确各通道的接口契约、数据模型与认证强度约束; 3. 为 REQ-认证-003(坐席端一键免登录)提供设计预研; 4. 为 REQ-认证-004(管理端一键免登录)保留设计边界(**不立项**)。 --- ## 2. 认证能力现状(技术视角) ### 2.1 扫码登录(REQ-认证-001,已上线 v0.7.0) | 项 | 内容 | |---|---| | 后端端点 | `/api/auth_qrcode/*`(v0.7.0 新增 4 个端点) | | 前端 | `frontend-agent/src/views/Login.vue`、`frontend-admin/src/views/Login.vue` 重写扫码 UI;`frontend-portal/src/views/QrcodeLogin.vue` | | 安全属性 | 双信道分离(手机认证 ≠ PC 操作)+ 人工主动扫码 = 弱 2FA / 带外确认 | | 部署 | 见 `部署运维/07-扫码登录OTP部署指南-v0.7.0.md` | ### 2.2 账号绑定(REQ-认证-002,已上线) - 坐席首次通过企微身份登录后,后端将其企微 `userid` 与系统 `agent` 账号建立绑定关系(写入 `agents` / 角色关联表)。 - 绑定是「一键免登录」「角色校验」「OTP 启用」的共同前置条件。 ### 2.3 OTP / MFA 二次验证(已上线 v0.7.0) | 项 | 内容 | |---|---| | 机制 | TOTP(pyotp / Google Authenticator),SMS(蜂鸟)作为备用通道 | | 数据模型 | `users.mfa_secret`、`users.mfa_enabled`、`users.mfa_bound_at`、`users.mfa_last_verified_at` | | 后端接口 | `/api/auth/otp-*`(6 个端点,见 `app/api/otp.py`)、`/api/auth/otp-admin-reset/{employee_id}`(管理员重置)、`/api/admin/high-risk/*`(高危操作 OTP 守卫 `require_high_risk_otp`) | | 登录流程 | admin 角色且 `mfa_enabled=1` 时,登录返回 `require_otp: True`,前端显示 OTP 输入框,验证通过才签发 token | | 角色差异 | 坐席 OTP **可选**(绑定后启用);admin OTP **强制常驻第二因子** | | 错误码 | 1006/1007/1008/1009/1010(OTP 相关) | > ⚠️ **文档一致性提示(2026-08-05 更新)**:`部署运维/06-OTP二次验证实现.md` 原使用 v0.5.6 旧命名 `/api/agents/otp-*`,已于 2026-08-05 修正为当前生产实现 **`/api/auth/otp-*`**(路由文件 `app/api/otp.py`,prefix=`/auth`)。部署指南 `07-扫码登录OTP部署指南-v0.7.0.md`、测试用例 `TC-REQ-认证-统一认证与登录-v1.0.md` 与前端 `frontend-agent/src/api/mfa.ts` 端点调用中原引用的更早规划命名 `/api/mfa/*`,已在本轮(2026-08-05)一并修正为 `/api/auth/otp-*`(AUTH-03 重构已用其取代原 `/mfa/*` 与 `/admin/mfa/*`)。当前活跃文档与实现已一致。 ### 2.4 企微 JS-SDK 免登录(`jsdk-login`,现成半截) | 项 | 内容 | |---|---| | 接口 | `POST /api/auth_wecom/jsdk-login` | | 行为 | 传入企微 `userid` → 后端查角色 → 直接返回 token(agent / admin / user) | | 现状 | 已在测试用例 JSDK-01~05 覆盖,是「一键免登录」后端的**现成半截**——缺前端在登录页主动拿 `userid` 的环节 | | 风险点 | 历史上 `EmergencyDispatcher.vue:113` 曾用未赋值的 `window.wecom_userid` 全局变量,导致拿不到 userid;**userid 必须来自标准 SDK 调用,禁止依赖全局变量** | ### 2.5 管理端登录基础序列 `技术架构/05-架构图/admin-login-sequence.mermaid` 描述基础序列:`/api/agents/login`(user_id + name)→ DB 查 role → Redis 存 token → 前端校验 `role==="admin"` → 存 `admin_token` → 跳转 `/admin/dashboard`。注意:该序列为 OTP 启用前的基础流,生产流已叠加 §2.3 的 OTP 步骤。 --- ## 3. 接口契约汇总 | 通道 | 端点 | 说明 | |---|---|---| | 扫码登录 | `/api/auth_qrcode/*`(4 端点) | v0.7.0 新增 | | 企微免密 | `POST /api/auth_wecom/jsdk-login` | 传 userid → 返回 token | | 账号密码 | `POST /api/agents/login` | 坐席/管理员账号密码 + OTP | | OTP | `/api/auth/otp-*`(6 端点) | 绑定/验证/解绑等(见 `app/api/otp.py`) | | OTP 重置 | `POST /api/auth/otp-admin-reset/{employee_id}` | 管理员重置 | | 高危守卫 | `/api/admin/high-risk/*` + `require_high_risk_otp` | 高危操作 OTP 守卫 | | 一键(预研) | `POST /api/auth/check-wecom-bind?userid=&role=agent`(待建) | REQ-003 新增 | **数据模型关键字段**:`agents.userid`(企微绑定)、`users.mfa_secret/enabled/bound_at/last_verified_at`、`role`(agent/admin/user)。 --- ## 4. 认证强度分级技术约束(对应 PRD §3) 以「风险敞口」而非「功能对称」分配认证强度: | 通道 | 技术约束 | |---|---| | 扫码登录 | 基准通道,始终保留;双信道弱 2FA | | 一键免登录(agentConfig) | **仅坐席端**;仅 PC 端、仅已绑定用户;token 有效期 < 8h(建议 2–4h + refresh);复用 `jsdk-login` 签发 | | 管理端登录 | OTP 常驻强制第二因子;**不套用一键免登录** | | 管理端一键(未来) | **不立项**;若未来立项,OTP 必须作为前置强制第二因子 | --- ## 5. REQ-认证-003 坐席端一键免登录 — 设计预研 ### 5.1 总体时序 ``` 登录页加载 → isWecomEnv()?(UA 含 wxwork) 否 → 走原扫码 是 → wx.config + wx.agentConfig(with_agent_config=true) 拿 userid → POST /api/auth/check-wecom-bind?userid=&role=agent has_bind=true → 显示「一键登录」→ 复用 jsdk-login 签发 token → 工作台 has_bind=false → 回落扫码 + 提示语 ``` ### 5.2 前端断点 - 文件:`frontend-agent/src/views/Login.vue` - 在现有 `isWecomEnv()` 分支内补齐 `wx.agentConfig` 调用链(签名复用 `wecom_jsapi.py` 的 `with_agent_config=true`,见 `wecom_jsapi.py:33`)。 - `userid` 必须来自 `wx.agentConfig` 回调,**禁止 `window.wecom_userid`**。 - UI:校验通过显示「🟢 一键登录」按钮;失败静默回落扫码。 ### 5.3 后端断点 - 新增 `POST /api/auth/check-wecom-bind`(参数 `userid` + `expected_role=agent`),返回 `has_bind` + `role`。 - 一键签发复用 `jsdk-login` 的 userid→token 逻辑(不新建签发路径)。 - H5 端无需改动(员工本就在企微内运行)。 ### 5.4 安全约束 - 仅对**已绑定用户**开放;token 有效期短于 8h(建议 2–4h + refresh)。 - 失败/异常一律回落扫码,不影响存量用户。 - 不引入 `window.wecom_userid` 全局变量(历史 bug 教训)。 ### 5.5 复用关系 - 复用:`isWecomEnv()`(Login.vue 已有)、`wecom_jsapi.py` 签名、`jsdk-login` 签发、角色校验表。 - 新增:仅 `check-wecom-bind` 一个接口 + Login.vue 的 agentConfig 调用与 UI。 --- ## 6. REQ-认证-004 管理端一键免登录 — 设计预留(不立项) **决策(2026-08-05,Q3)**:**不立项**。管理端风险敞口(系统配置/权限/全局数据)远大于坐席端,按 §4 分级不套用一键免登录。 仅作未来设计边界预留(不进入排期): - 若未来立项,必须复用 REQ-003 的企微身份探测 + 将既有 OTP(`/api/auth/otp-*`)作为**前置强制第二因子**; - 新增部分仅为「企微身份一键探测 + 复用既有 OTP 作前置网关」,非从零建设 OTP。 --- ## 7. 非目标 1. REQ-003 仅预研,本次不开发; 2. 管理端一键(REQ-004)不立项; 3. 不改变扫码登录(基准通道); 4. 不新建 OTP 体系(复用既有 `/api/auth/otp-*`); 5. 不涉及 H5 登录改造。 --- ## 8. 风险与缓解 | 风险 | 缓解 | |---|---| | 单设备被控 → 攻击者可无手机配合新发起会话 | 仅已绑定用户 + 短时效 token + 设备绑定(P2) | | agentConfig 调用失败 | 静默回落扫码 | | `window.wecom_userid` 未赋值 | 禁止全局变量,强制标准 SDK 调用 | | 06 文档端点命名偏差(旧 `/api/agents/otp-*`) | 已于 2026-08-05 修正为生产实现 `/api/auth/otp-*`;07 部署指南/TC 测试用例/前端 `mfa.ts` 端点调用中的 `/api/mfa/*` 规划命名亦于本轮(2026-08-05)一并修正,活跃文档与实现已一致 | --- ## 9. 关联文档索引 | 文档 | 路径 | 用途 | |---|---|---| | PRD | `../01-产品文档/01-认证与登录/PRD-REQ-认证-统一认证与登录-v1.1.md` | 产品需求 | | 测试用例 | `../03-测试文档/03-功能测试用例/TC-REQ-认证-统一认证与登录-v1.0.md` | 功能用例 | | OTP 实现 | `部署运维/06-OTP二次验证实现.md` | OTP 机制(端点命名待对齐) | | 扫码+OTP 部署 | `部署运维/07-扫码登录OTP部署指南-v0.7.0.md` | 部署与现状 | | admin 登录序列 | `技术架构/05-架构图/admin-login-sequence.mermaid` | 序列图 | --- ## 10. 修订历史 | 版本 | 日期 | 变更内容 | 修改人 | |---|---|---|---| | v1.0 | 2026-08-05 | 初始技术方案:收敛认证技术设计、接口契约、认证强度分级约束、REQ-003 设计预研、REQ-004 不立项边界 | 宋献 |