facc04aa65
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
187 lines
9.6 KiB
Markdown
187 lines
9.6 KiB
Markdown
# 技术方案-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 不立项边界 | 宋献 |
|