Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26769fb3be | ||
|
|
4d6dfa5025 |
@@ -123,7 +123,7 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
|
||||
```
|
||||
|
||||
- Claude Code:`claude mcp add --transport http xiaoqu https://mcp.<域名>/mcp --header "Authorization: Bearer <ak>"`。
|
||||
- ak 明文只出现在本机客户端配置与签发时刻;服务端只存 SHA-256 哈希,含 ak 的客户端配置文件不得提交版本库。
|
||||
- ak 明文只出现在服务端签发响应和运行时;服务端保存 SHA-256 哈希及加密密文,含 ak 的客户端配置文件不得提交版本库。
|
||||
|
||||
### 5.2 Web 管理后台(四个页面)
|
||||
|
||||
@@ -155,7 +155,7 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
|
||||
- **`POST /api/open/token/ensure`**(get-or-create):
|
||||
- 入参:`mobile`、`email`(至少一个,手机号优先)、`scopeCode`(作用域编码,必填)。
|
||||
- 人员只认企业账号(`enterprise_type` 0/1,NULL 保留,与后台用户列表同口径);查不到 → 404。
|
||||
- 该人员在该作用域下**已存在**有效 Token(正常且未过期)→ `created=false`,只回 `keyPrefix`/`expireTime` 等元信息。**明文不可回吐**:服务端只存 SHA-256,这是安全红线,调用方丢了明文只能走后台重新签发。
|
||||
- 该人员在该作用域下**已存在**有效 Token(正常且未过期)→ `created=false`,只回 `keyPrefix`/`expireTime` 等元信息,明文不回传。
|
||||
- **不存在** → 自动创建永久 Token(一作用域一 Token,memo 标记 `open-api 自动签发`),`created=true` 返回明文,仅此一次。
|
||||
- 全程写 `audit_log`(`tool_name=open.token.ensure`,`client_type=open-api`,参数摘要脱敏)。
|
||||
|
||||
@@ -240,7 +240,7 @@ xiaoqu-agent-service/
|
||||
- 不依赖老 jar,Entity 自行生成 → 与老系统零代码依赖
|
||||
- 前后端一体打包单 jar → 可逆决定,将来可拆
|
||||
- 一作用域一 Token(2026-09-04)→ 收回某能力不需整把吊销;数据模型不变(token_scope 每把只写一行)
|
||||
- Open API 已存在 Token 只回元信息不回明文 → 服务端只存哈希,明文不可回吐是安全红线
|
||||
- 长期 Token 仅按用户首次创建返回明文一次;后续复用元信息,不对外提供二次获取明文接口(服务端仅用于禁用/审计与治理)
|
||||
- Open API 鉴权用配置文件静态密钥(dev)→ 服务间调用方少,落库+轮换留给生产阶段
|
||||
- 列表类接口一律服务端分页,pageSize/rows 上限 100(超出截断为 100)→ 防止一次性拉全表,MCP 工具与后台 API 同一尺度
|
||||
|
||||
@@ -266,4 +266,3 @@ xiaoqu-agent-service/
|
||||
- 合并回 main 后用 `xiaoqu-agent-service/deploy/deploy.sh restart` 完成构建+重启,接口实测通过再 `git push`。
|
||||
- 后台列表页统一服务端分页(page 从 1 开始,pageSize 默认 20、上限 100);MCP 工具 `rows` 同样上限 100,超出截断并在 `notice` 字段提示。
|
||||
- **新增/修改 MCP 工具必须同步更新 `doc/agent-mcp-tools-reference.md`**(入参、出参、权限语义、示例),工具对外契约以该文档为准。
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 端点 | `POST http://<host>:8088/mcp`(MCP Streamable HTTP) |
|
||||
| 认证 | Header `Authorization: Bearer xqak_xxx`,ak 在管理后台「用户与 Token」签发,服务端只存 SHA-256 |
|
||||
| 认证 | Header `Authorization: Bearer xqak_xxx`,Token 由管理后台或受信任 Open API 获取;服务端保存 SHA-256 及加密密文 |
|
||||
| 工具可见性 | `tools/list` 按 ak 作用域过滤,只返回该 ak 作用域并集内且启用状态的工具(2026-09-04 起,服务端 servlet 层重写响应实现);`tools/call` 另有强制校验兜底,越权返回可读拒绝信息并落审计(denied) |
|
||||
| 行级数据权限 | 跟 ak 所属人走,照搬老系统 `getByIsAgent` 语义(见设计文档 §3.3),与作用域无关 |
|
||||
| 分页 | 列表类工具统一 `page`(从 1 开始,默认 1)、`rows`(默认 20,**上限 100**,超出截断为 100 并在返回 `notice` 字段提示) |
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
| MCP Token 过期/401 | 清除当前用户 MCP Token,重新换取一次;同一请求不自动重放工具副作用 |
|
||||
| accessToken 刷新 | 保留 MCP Token;若服务返回 401,再重新换取 |
|
||||
| 切换账号 | 先断开旧 MCP Client,再加载新账号 Token 并建立新 Client |
|
||||
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 不会自动失效,需要显式调用 revoke |
|
||||
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 以服务端状态为准,不做客户端侧远程失效调用 |
|
||||
| 多账号 | Keychain key 必须包含稳定用户 ID,禁止所有账号共用 `mcpToken` |
|
||||
|
||||
~~建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。~~
|
||||
@@ -115,29 +115,8 @@
|
||||
- 不存在则自动创建永久 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 关联,加密存储),之后下发给本用户的客户端。
|
||||
|
||||
**身份约束**:业务后端不得接受客户端或模型传入的 `user_id`、`mobile`、`email` 作为身份依据。必须先从已验证的 xiaoqu-ai `accessToken` 会话解析用户,再由业务后端查询该用户的手机号/邮箱并调用 `ensure`。`X-Open-Key` 只允许服务端保管。
|
||||
|
||||
### 4.1a Token 丢失与主动失效(建议补充)
|
||||
|
||||
当前 `ensure` 在 `created=false` 时不会返回明文,无法解决业务后端首次落库失败或 Token 泄露后的换发问题。建议增加两个服务间接口,均使用 `X-Open-Key` 鉴权:
|
||||
|
||||
`POST /api/open/token/rotate`
|
||||
|
||||
- 按用户和 `scopeCode` 禁用当前有效 Token,创建新 Token。
|
||||
- 仅本次返回新 Token 明文;旧 Token 立即失效。
|
||||
- 记录旧 Token、新 Token 前缀、用户和 scope,禁止记录 Token 明文。
|
||||
- 具备幂等保护,避免并发换发产生多个有效 Token。
|
||||
|
||||
`POST /api/open/token/revoke`
|
||||
|
||||
- 按用户和 `scopeCode` 禁用当前 Token。
|
||||
- 不返回 Token 明文;重复调用视为成功。
|
||||
- 用于退出登录后的主动失效、设备解绑和安全事件处置。
|
||||
|
||||
在 `rotate/revoke` 尚未实现前,退出登录只能清理客户端本地凭证,不能声称服务端 Token 已失效;管理员后台禁用仍是唯一服务端回收方式。
|
||||
|
||||
### 4.1b xiaoqu-ai 业务后端门面(待 xiaoqu-ai 侧实现)
|
||||
|
||||
iOS 面向的接口(建议沿用原设计):POST `/api/v1/mcp/session-token`,`Authorization: Bearer <xiaoqu-access-token>`。业务后端从登录态解析用户 → 服务端查询手机号/邮箱 → 调 `ensure` → 返回 `{token, expires_at, user_id}`。不得使用请求体中的 `user_id/mobile/email` 覆盖登录态身份;客户端身份字段(client、device_id)由业务后端自行记录,agent-service 不感知。
|
||||
@@ -231,12 +210,12 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
| 文件/目录 | 层级 | 类型 | 改动说明 |
|
||||
|---|---|---|---|
|
||||
| ~~`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/OpenTokenController.java` 等 | Open API | **ensure 已交付** | `ensure` 使用 X-Open-Key 鉴权 |
|
||||
| `open/OpenKeyFilter.java` | Filter | **已交付** | 服务间静态密钥,常量时间比较,配置为空即关闭 |
|
||||
| `admin/AdminTokenService.java` | Service | **已交付** | 一作用域一 Token;`issueSingle`/`findActiveToken` 供 ensure 复用 |
|
||||
| `admin/AdminTokenService.java` | Service | **ensure 已交付;密文保存待实现** | 一作用域一 Token;签发时保存哈希和加密密文 |
|
||||
| `common/GlobalExceptionHandler.java` | Handler | **已交付** | 错误统一 `{"message": ...}` |
|
||||
| `security/AkAuthFilter.java` | Filter | 保持兼容 | `/mcp` Bearer ak 校验不变 |
|
||||
| `src/test/...open` | Test | **已交付** | ensure 已存在/新建/404/停用作用域/缺参 5 个用例 |
|
||||
| `src/test/...open` | Test | **部分已交付** | 覆盖 ensure 的参数、已存在/新建/404/停用/缺参测试 |
|
||||
|
||||
### xiaoqu-ai
|
||||
|
||||
@@ -256,6 +235,7 @@ Token 不写入该文件。客户端运行时读取当前用户 Token,并动
|
||||
|---|---|---:|---|---|
|
||||
| 1 | ~~服务端确认 JWT 与租户映射、Token exchange 契约~~ → xiaoqu-ai 业务后端实现门面接口(登录态 → ensure) | 2h | - | 门面 401/403 语义确定;明文按 user_id 加密落库 |
|
||||
| 2 | ~~服务端实现用户 MCP Token 签发与审计~~ **已交付**(ensure + 审计,2026-09-04) | - | - | 已上线并实测幂等/脱敏/错误语义 |
|
||||
| 2b | ~~服务端增加二次获取能力~~ | - | - | 已按当前边界关闭,不再新增接口 |
|
||||
| 3 | iOS Keychain 账号隔离和认证生命周期接入 | 3h | #1 | 登录/切换/退出无旧 Token 残留 |
|
||||
| 4 | Rust MCP HTTP Bearer、Session、初始化通知 | 3h | #3 | initialize/list/call 真实握手成功 |
|
||||
| 5 | MCP 工具到 Server 路由映射 | 2h | #4 | 多 Server 下工具调用不发错目标 |
|
||||
|
||||
@@ -44,8 +44,9 @@ Content-Type: application/json
|
||||
|
||||
- `X-Open-Key` 只能配置在受信任后端,不能下发 App。
|
||||
- 业务后端必须从已验证用户会话查询手机号/邮箱,不能信任客户端或模型传入的身份字段。
|
||||
- `created=true` 才返回 Token 明文;业务后端必须立即加密保存。
|
||||
- `created=false` 不返回明文;Token 丢失前需先实现 rotate,退出登录需实现 revoke 才能立即失效。
|
||||
- 首次创建返回 Token 明文;xiaoqu-mcp 同时保存 SHA-256 哈希和 KMS/独立密钥加密密文。
|
||||
- 已存在 Token 不回传明文,重复调用返回元信息并复用。
|
||||
- Token 密文缺失或解密失败必须报错;不得回退到哈希值或公共 Token。
|
||||
|
||||
## 4. 推荐实现
|
||||
|
||||
@@ -73,7 +74,7 @@ Content-Type: application/json
|
||||
- `src/ops_assistant_service/mcp_token_provider.py`
|
||||
- 接收已验证的 `SharedIdentity`。
|
||||
- 从受信任用户资料源取得 mobile/email。
|
||||
- 调用 `ensure`,首次创建时加密持久化明文。
|
||||
- 调用 `ensure`,首次签发返回明文;后续直接复用本地持久化 token。
|
||||
- 不接受 `user_id/mobile/email` 作为客户端覆盖参数。
|
||||
|
||||
不引入通用 MCP 框架、第二个 Agent 或数据库直连;现有 `httpx` 已足够完成首期只读接入。
|
||||
@@ -116,7 +117,7 @@ validate_tool_input
|
||||
|
||||
- 登录用户变化:先关闭旧 Session,再建立新用户 Session。
|
||||
- MCP 返回 401:清理当前用户 Token,最多执行一次重新获取和重连;不自动重放副作用请求。
|
||||
- 退出登录:清理本地/服务端 Token 缓存;实现 revoke 后再主动撤销远端 Token。
|
||||
- 退出登录:清理本地/会话 Token 缓存;如需远端立即失效,走服务端管理端吊销能力。
|
||||
- 日志只记录用户 ID、工具名、scope 和错误码,不记录 Token、Authorization Header 或手机号/邮箱明文。
|
||||
- `tools/list` 仅用于校验远端能力;真正可调用集合以本地 allowlist 为准。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user