文档:两份前端文档按真环境核对 —— clientId 实测为 task-dispatcher,示例换成实测响应

设计阶段写的两份对外文档,Task 12 只按契约核过,没按真环境核过。这次跑通之后逐条对:
① clientId 写的是「建议 api-gateway」——实际这个 realm 里 api-gateway 的 mapper 对我们不适用,
   本部署是 task-dispatcher(两个前端 client 都已挂好指向它的 audience mapper,实测确认);
② e2e 账号写的是 bpos-e2e,实际用的是 task-dispatcher 的服务账号,且它只有 service+user
   两个角色 → 只进得了工作台,要跑测试台得另配;
③ 「ends 为空」原本像个假想边界,实际 service 角色就是——没配进「角色→端」表的角色一律不加端;
④ §8 的响应示例从「按契约构造」换成实测响应(/api/me 的 JIT 建档结果、403 的真 message、
   aud 不对时的 401、票据的真形状);
⑤ 两处「核对状态」改写:前置已就位、整条链真环境验过,并写明仍未实测的那一件——用真人 token 走一遍。
This commit is contained in:
xcs
2026-08-26 10:55:58 +08:00
parent f70de44338
commit 0f1e53d114
2 changed files with 43 additions and 13 deletions
+13 -5
View File
@@ -10,10 +10,13 @@
- 后端**不做登录**。前端(公司已在 Keycloak `corp` realm 建好两个前端 client:`web-portal` 用户端
`jcai-bpos.akhr-ai.com` / `web-console` 管理端 `jcai-bpos-opt.akhr-ai.com`,PKCE)自己拿 token。
- token 的 `aud` 必须含本后端的 clientId(后端环境变量 `KEYCLOAK_CLIENT_ID`,建议 `api-gateway`);
- token 的 `aud` 必须含本后端的 clientId —— **本部署是 `task-dispatcher`**(后端环境变量 `KEYCLOAK_CLIENT_ID`);
`web-portal` 与 `web-console` 都已挂好指向它的 audience mapper(2026-08-26 实测确认),前端不用为此做任何事;
后端**不看 `azp`**,用哪个前端 client 取的 token 都行 —— 没有 aud 会全线 401,后端的 401 message 会点名让管理员加 audience mapper。
- 每个请求头带 `Authorization: Bearer …`。`GET /api/me` 回答两件事:我是谁、我能进哪几个端(`ends`)。
- **权限只到端**:`design`(设计中心 `/design`)/ `ops`(运营中心 `/ops`)/ `workbench`(工作台 `/`)。
当前 realm 里的角色不止 `admin` / `user` 两种(服务账号还持有 `service`),而**没配进「角色 → 端」表的角色一律不加端** ——
所以 `ends: []` 不是假想的边界,是真会遇到的(§3 那一段说了怎么呈现)。
入口亮不亮、页面能不能进,只看 `ends`;进去之后哪个接口能调,后端按每条路由的归属自己判(§6 总表)。
- 两条带不了 Authorization 头的路(`EventSource` 与 `<a href download>`)用 **60 秒票据**:
`GET /api/auth/ticket` → 把 `ticket` 放到那条 GET 的 `?ticket=` 上。
@@ -24,7 +27,7 @@
```
浏览器(SPA,PKCE)──登录/刷新 token──▶ https://keycloak.akhr-ai.com/realms/corp
│
│ Authorization: Bearer <access_token>(aud ∋ api-gateway)
│ Authorization: Bearer <access_token>(aud ∋ task-dispatcher)
▼
apps/api 鉴权中间件(唯一一处)
① 路由归属:契约里每条路由自带 access(public / any / 端)
@@ -183,7 +186,9 @@ async function openStream(onTouch: (t: Touch) => void, onOpen: () => void): Prom
可选 `KEYCLOAK_PROVISIONING_CLIENT_ID/_SECRET`(同步名册)、`JCAI_TEST_ACCOUNTS`(测试账号用户名)。
- Vite 的 `/api` 反代不变;浏览器直连 `keycloak.akhr-ai.com` 取 token,与后端无关(跨域由 Keycloak 的 Web Origins 管,手册有写)。
- 想在没前端的情况下打接口:用服务账号拿 token(见 API 文档 §8 的 curl),或让管理员临时给 `web-portal` 开 Direct Access Grants 用密码取一枚(手册 §6.1,用完关掉)。
- 试跑 / e2e 用的「AI 账号」现在是 Keycloak 里的服务账号(`bpos-e2e`),工作台测试时用它的 token。
- 试跑 / e2e 用的是 **`task-dispatcher` 的服务账号**(`service-account-task-dispatcher`)。
注意它当前只有 `service` + `user` 两个 realm 角色 → 只进得了工作台;要跑测试台(设计端)得在运营中心
给 `service` 配上 design,或给该账号加 `admin`。
## 9. 接入完成的判据
@@ -201,5 +206,8 @@ async function openStream(onTouch: (t: Touch) => void, onOpen: () => void): Prom
Task 12 已用契约把本文核过一遍:§2 的十个文件指针全部存在(`apps/web` 本轮未动,指针原样有效);
§7 的退役路由与替代逐条对过 `docs/API文档-鉴权与人员.md` §6 的总表,一致。
本文里出现的响应示例(`GET /api/me`、票据、403 detail)与 API 文档同源,同样按契约构造 ——
换真实响应要 Keycloak 侧前置 1 与 4(服务账号 client、audience mapper)先就位,本轮未就位,留给前置就位后去 API 文档 §8 换。
**2026-08-26 补:整条链已在真环境跑通并实测。** Keycloak 侧前置已就位(`task-dispatcher` 及两个前端 client
的 audience mapper 齐备),后端以 `KEYCLOAK_CLIENT_ID=task-dispatcher` 启动、自检通过。实测验过的行为:
验签 → claims 归一 → 首次登录 JIT 建档(落 `NONE / 未分配` 岗)→ 角色→端 → 端判定;工作台 200、运营中心 403(带 `detail`);
票据签发后 `?ticket=` 真连上 SSE、同一张票据用在 POST 上被拒;`HEAD` 与 `GET` 判定一致。
本文与 API 文档 §8 的响应示例现在都是**实测响应**,不再是按契约构造的样例。