docs: iOS每用户Token设计对齐已交付ensure接口(两段式/明文不可回吐/业务后端门面); 修tools/list不按scope过滤的文档漂移
This commit is contained in:
@@ -69,6 +69,8 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
|
||||
4. 执行查询,按"人"的数据权限规则加行过滤
|
||||
5. 审计落库(人、token、工具、入参摘要、结果、耗时)
|
||||
|
||||
注:`tools/list` 不按作用域过滤(全量已注册工具对所有 ak 可见),第 3 步的强制点在 `tools/call`——越权调用得到的是可读的拒绝信息 + denied 审计,而不是"工具不存在"。
|
||||
|
||||
### 3.3 行级数据权限移植规范(照搬现有系统)
|
||||
|
||||
来源:`WrapperUtils.getByIsAgent` 系列 + `authority.isagent`。规则:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|---|---|
|
||||
| 端点 | `POST http://<host>:8088/mcp`(MCP Streamable HTTP) |
|
||||
| 认证 | Header `Authorization: Bearer xqak_xxx`,ak 在管理后台「用户与 Token」签发,服务端只存 SHA-256 |
|
||||
| 工具可见性 | 按 ak 绑定的作用域过滤,`tools/list` 只返回该 ak 可用的工具 |
|
||||
| 工具可见性 | `tools/list` 返回全部已注册工具(**不按作用域过滤**);作用域在 `tools/call` 时服务端强制校验,越权返回可读拒绝信息并落审计(denied) |
|
||||
| 行级数据权限 | 跟 ak 所属人走,照搬老系统 `getByIsAgent` 语义(见设计文档 §3.3),与作用域无关 |
|
||||
| 分页 | 列表类工具统一 `page`(从 1 开始,默认 1)、`rows`(默认 20,**上限 100**,超出截断为 100 并在返回 `notice` 字段提示) |
|
||||
| 分页出参 | 统一含 `total`(总数)、`page`、`rows`、`records`(本页记录)、`notice`(仅截断时出现) |
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
2. 当前 FFI 将 `apiKey` 发送为 `X-API-Key`,与服务端要求的 Bearer 不一致。
|
||||
3. iOS FFI 未把配置文件中的 MCP Server 自动接入 `FfiEngineConfig`。
|
||||
4. 多 MCP Client 共存时,工具调用没有稳定的“工具名 → 所属 Server”映射。
|
||||
5. 当前管理端签发接口需要管理员会话,不适合作为普通用户登录后的自动取 Token 接口。
|
||||
5. ~~当前管理端签发接口需要管理员会话,不适合作为普通用户登录后的自动取 Token 接口。~~ **已解决(2026-09-04)**:xiaoqu-agent-service 已交付 Open API `POST /api/open/token/ensure`(get-or-create,服务间密钥鉴权),见 §4.1。
|
||||
|
||||
## 3. 方案设计
|
||||
|
||||
@@ -70,46 +70,56 @@
|
||||
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;服务端撤销或等待短期过期 |
|
||||
| 多账号 | Keychain key 必须包含稳定用户 ID,禁止所有账号共用 `mcpToken` |
|
||||
|
||||
建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。
|
||||
~~建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。~~
|
||||
|
||||
**2026-09-04 更新(与已交付实现对齐)**:现行模型为**长期有效 + 禁用回收**(与管理后台签发模型一致),不做 15 分钟短周期。`ensure` 语义天然兼容短周期模式(过期 token 不计入"已存在",会触发新建并返回新明文),将来如需缩短有效期,只调整签发时的 `expireTime`,协议不变。
|
||||
|
||||
## 4. API 契约
|
||||
|
||||
### 4.1 POST `/api/v1/mcp/session-token`
|
||||
> **2026-09-04 更新**:本节按 xiaoqu-agent-service 已交付的 Open API 重写。核心变化——agent-service 不直接验证 xiaoqu-ai 登录态(它没有 JWT 基础设施,也不懂租户),改为**两段式**:iOS 用登录态调 xiaoqu-ai 业务后端;业务后端拿着登录用户的手机号/邮箱,用服务间密钥调 agent-service 的 `ensure` 接口取 MCP Token。
|
||||
|
||||
描述: 使用当前 xiaoqu-ai 登录态,为当前用户签发一个短期 MCP Token。
|
||||
### 4.1 服务端原语(已交付):POST `/api/open/token/ensure`
|
||||
|
||||
描述: 按 手机号/邮箱 + 作用域编码 确保用户有有效 MCP Token(get-or-create)。
|
||||
|
||||
认证:
|
||||
|
||||
- `Authorization: Bearer <xiaoqu-access-token>`
|
||||
- 服务端必须从签名 Token 解析用户和租户,忽略请求体中的身份字段。
|
||||
- `X-Open-Key: <服务间静态密钥>`(dev 配置在 `application-dev.yml` 的 `app.open.api-key`,生产走环境变量;配置为空 = 接口关闭)。
|
||||
- 只允许 xiaoqu-ai 业务后端等服务端调用方持有,**不得下发到 App**。
|
||||
|
||||
请求体:
|
||||
|
||||
- `client`: string (required) — 固定为 `xiaoqu-ai-ios`,最长 64 字符。
|
||||
- `device_id`: string (required) — 设备安装实例 ID,最长 128 字符;不得包含 Token。
|
||||
- `mobile`: string — 登录用户手机号;与 `email` 至少传一个,手机号优先。
|
||||
- `email`: string — 登录用户邮箱。
|
||||
- `scopeCode`: string (required) — 作用域编码(如 `operation`);iOS 用哪个作用域由双方约定后在此固定。
|
||||
|
||||
响应 200:
|
||||
|
||||
- `token`: string (required) — MCP 明文 Token,仅本次返回。
|
||||
- `expires_at`: datetime (required) — MCP Token 到期时间。
|
||||
- `user_id`: int (required) — 服务端从登录态解析出的用户 ID。
|
||||
- `tenant_id`: int (required) — 服务端解析出的租户 ID。
|
||||
- `scope_version`: string (required) — 本次绑定权限版本,用于客户端诊断,不作为鉴权依据。
|
||||
- `created`: bool — 本次是否新建。
|
||||
- `token`: string | null — **仅 `created=true` 时返回明文,且只出现这一次**;`created=false` 时恒为 null。
|
||||
- `keyPrefix`: string — Token 前缀(如 `xqak_abcd1234`),用于标识和对账。
|
||||
- `expireTime`: datetime | null — 现行为 null(永久,靠禁用回收)。
|
||||
- `userId` / `userName` / `scopeCode`: 归属信息。
|
||||
|
||||
错误:
|
||||
错误(统一 `{"message": "原因"}`):
|
||||
|
||||
- `401`: 登录态缺失、无效或已过期。
|
||||
- `403`: 用户无权使用 MCP 或租户未开通。
|
||||
- `409`: 用户 Token 配置缺失/作用域未配置。
|
||||
- `429`: 换取频率超限。
|
||||
- `503`: MCP Token 服务暂不可用;客户端不得携带旧 Token 强行重试写操作。
|
||||
- `400`: 参数缺失 / 作用域不存在或已停用。
|
||||
- `401`: `X-Open-Key` 无效。
|
||||
- `403`: Open API 未启用(服务端未配置密钥)。
|
||||
- `404`: 人员不存在或非企业账号(只认 `enterprise_type` 0/1 的企业账号)。
|
||||
|
||||
服务端处理要求:
|
||||
服务端处理(已实现):
|
||||
|
||||
- 使用现有 `agent_api_key` 哈希存储和 `token_scope` 绑定逻辑。
|
||||
- 每次签发生成新的随机 Token,明文仅返回一次;不能查询旧 Token 明文。
|
||||
- 默认只绑定当前用户允许的只读作用域;高风险作用域必须单独审批。
|
||||
- 签发、刷新、撤销写入审计日志,记录 `user_id`、`tenant_id`、client、device_id、结果和原因,不记录 Token。
|
||||
- 复用 `agent_api_key` 哈希存储与 `token_scope` 绑定;一作用域一 Token。
|
||||
- 已存在有效 Token(正常且未过期)只回元信息——**明文不可回吐,服务端只存 SHA-256**。
|
||||
- 不存在则自动创建永久 Token,memo 标记 `open-api 自动签发(<scopeCode>)`。
|
||||
- 全程写 `audit_log`(`tool_name=open.token.ensure`、`client_type=open-api`,手机号/邮箱脱敏,不记录 Token)。
|
||||
|
||||
**关键约束与对策**:`created=false` 时拿不到明文。因此 xiaoqu-ai 业务后端必须在首次 `created=true` 时**自己持久化明文**(按 user_id 关联,加密存储),之后下发给本用户的客户端;若明文丢失,只能由管理员在后台禁用旧 Token 后再调 `ensure` 触发新建(或将来加 rotate 接口)。
|
||||
|
||||
### 4.1b xiaoqu-ai 业务后端门面(待 xiaoqu-ai 侧实现)
|
||||
|
||||
iOS 面向的接口(建议沿用原设计):POST `/api/v1/mcp/session-token`,`Authorization: Bearer <xiaoqu-access-token>`。业务后端从登录态解析用户 → 取手机号/邮箱 → 调 `ensure` → 返回 `{token, expires_at, user_id}`。客户端身份字段(client、device_id)由业务后端自行记录,agent-service 不感知。
|
||||
|
||||
### 4.2 POST `/mcp`
|
||||
|
||||
@@ -184,6 +194,8 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
|
||||
### 6.2 建议新增字段(仅在现有字段不足时)
|
||||
|
||||
> 2026-09-04 注:已交付实现暂不新增字段——ensure 自动签发的 Token 用 `memo` 标记(`open-api 自动签发(<scopeCode>)`),`create_id=0` 表示系统签发。若后续需要按来源/设备撤销或统计,再按下表加独立 migration。
|
||||
|
||||
| 表 | 字段 | 类型/约束 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `agent_api_key` | `source` | `VARCHAR(32) NOT NULL DEFAULT 'admin'` | `admin` / `user_session`,便于撤销和审计 |
|
||||
@@ -197,12 +209,13 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
|
||||
| 文件/目录 | 层级 | 类型 | 改动说明 |
|
||||
|---|---|---|---|
|
||||
| `security/` JWT 验证组件 | Security | 新增/修改 | 验证 xiaoqu 登录态并解析 user/tenant,拒绝客户端自报身份 |
|
||||
| `mcp/` Token exchange service | Service | 新增 | 签发短期用户 MCP Token、作用域绑定、限流和审计 |
|
||||
| `handler/` MCP credential controller | Handler | 新增 | 实现 `/api/v1/mcp/session-token` |
|
||||
| `security/AkAuthFilter.java` | Filter | 修改 | 保持 Bearer MCP AK 兼容,补齐撤销/错误审计要求 |
|
||||
| `db/migration/V__user_session_token.sql` | Migration | 条件新增 | 只有字段/索引确有需要时新增 |
|
||||
| `src/test/...` | Test | 新增 | 用户隔离、过期、撤销、越权、限流和不记录明文测试 |
|
||||
| ~~`security/` JWT 验证组件~~ | Security | ~~新增~~ 取消 | agent-service 不验证 xiaoqu-ai 登录态,改由业务后端两段式调用 |
|
||||
| `open/OpenTokenController.java` 等 | Open API | **已交付**(2026-09-04) | `POST /api/open/token/ensure`,X-Open-Key 鉴权,get-or-create |
|
||||
| `open/OpenKeyFilter.java` | Filter | **已交付** | 服务间静态密钥,常量时间比较,配置为空即关闭 |
|
||||
| `admin/AdminTokenService.java` | Service | **已交付** | 一作用域一 Token;`issueSingle`/`findActiveToken` 供 ensure 复用 |
|
||||
| `common/GlobalExceptionHandler.java` | Handler | **已交付** | 错误统一 `{"message": ...}` |
|
||||
| `security/AkAuthFilter.java` | Filter | 保持兼容 | `/mcp` Bearer ak 校验不变 |
|
||||
| `src/test/...open` | Test | **已交付** | ensure 已存在/新建/404/停用作用域/缺参 5 个用例 |
|
||||
|
||||
### xiaoqu-ai
|
||||
|
||||
@@ -220,8 +233,8 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
|
||||
| # | 任务标题 | 工时 | 依赖 | 完成标准 |
|
||||
|---|---|---:|---|---|
|
||||
| 1 | 服务端确认 JWT 与租户映射、Token exchange 契约 | 2h | - | 401/403/409/429 语义和 scope 默认值确定 |
|
||||
| 2 | 服务端实现用户 MCP Token 签发与审计 | 4h | #1 | Token 只返回一次,哈希落库,绑定 user/tenant/scope |
|
||||
| 1 | ~~服务端确认 JWT 与租户映射、Token exchange 契约~~ → xiaoqu-ai 业务后端实现门面接口(登录态 → ensure) | 2h | - | 门面 401/403 语义确定;明文按 user_id 加密落库 |
|
||||
| 2 | ~~服务端实现用户 MCP Token 签发与审计~~ **已交付**(ensure + 审计,2026-09-04) | - | - | 已上线并实测幂等/脱敏/错误语义 |
|
||||
| 3 | iOS Keychain 账号隔离和认证生命周期接入 | 3h | #1 | 登录/切换/退出无旧 Token 残留 |
|
||||
| 4 | Rust MCP HTTP Bearer、Session、初始化通知 | 3h | #3 | initialize/list/call 真实握手成功 |
|
||||
| 5 | MCP 工具到 Server 路由映射 | 2h | #4 | 多 Server 下工具调用不发错目标 |
|
||||
@@ -250,8 +263,14 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
### 9.3 联调命令
|
||||
|
||||
```bash
|
||||
# 使用当前用户登录态换 MCP Token(示例,不把 Token 写入仓库)
|
||||
curl -X POST http://<host>/api/v1/mcp/session-token \
|
||||
# 业务后端侧:用服务间密钥 ensure 用户 MCP Token(示例,密钥与明文不写入仓库)
|
||||
curl -X POST http://<host>:8088/api/open/token/ensure \
|
||||
-H 'X-Open-Key: <服务间密钥>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"mobile":"<登录用户手机号>","scopeCode":"operation"}'
|
||||
|
||||
# iOS 侧:走 xiaoqu-ai 业务后门门面(待实现)
|
||||
curl -X POST http://<xiaoqu-ai-host>/api/v1/mcp/session-token \
|
||||
-H 'Authorization: Bearer <xiaoqu-access-token>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"client":"xiaoqu-ai-ios","device_id":"<device-id>"}'
|
||||
|
||||
Reference in New Issue
Block a user