Files
lijiaoqiao/docs/token_auth_middleware_design_v1_2026-03-29.md

123 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 平台鉴权与 Token 校验中间件设计TOK-002
- 版本v1.0
- 日期2026-03-29
- 状态:开发实施设计基线
- 依赖:`docs/token_runtime_minimal_spec_v1.md`
- 目标:实现“仅平台凭证入站”,并为 M-014/M-016/M-021 提供可验证链路。
## 1. 设计目标
1. 所有北向请求必须通过平台凭证校验。
2. 外部 `query key` 入站一律拒绝并记录审计事件。
3. 鉴权结果可追踪到 `request_id + subject_id + token_id`
4. 在不泄露上游凭证的前提下返回标准错误码。
## 2. 适用范围
1. 路由范围:`/api/v1/supply/*``/api/v1/platform/*`
2. 鉴权头:仅支持 `Authorization: Bearer <token>`
3. 排除范围:健康检查、内部探针、公开静态资源。
## 3. 中间件链路
## 3.1 处理顺序
1. `RequestIdMiddleware`
2. `QueryKeyRejectMiddleware`
3. `BearerExtractMiddleware`
4. `TokenVerifyMiddleware`
5. `TokenStatusCheckMiddleware`
6. `ScopeRoleAuthzMiddleware`
7. `AuditEmitMiddleware`
## 3.2 关键规则
1. `QueryKeyRejectMiddleware`
- 拒绝任意 `?key=``?api_key=``?token=` 形式外部参数。
- 返回 `401 QUERY_KEY_NOT_ALLOWED`
2. `BearerExtractMiddleware`
-`Authorization` 直接 `401 AUTH_MISSING_BEARER`
3. `TokenVerifyMiddleware`
- 校验签名、`iss``aud``exp``nbf``jti`
- 签名失败返回 `401 AUTH_INVALID_TOKEN`
4. `TokenStatusCheckMiddleware`
- 查询 token 状态缓存(`active/revoked/expired`)。
- `revoked/expired` 返回 `401 AUTH_TOKEN_INACTIVE`
5. `ScopeRoleAuthzMiddleware`
- 按路由匹配 scope不足返回 `403 AUTH_SCOPE_DENIED`
## 4. 数据与缓存策略
1. 状态源:`platform_token_registry`(运行态主表)。
2. 热缓存:`token_status_cache`TTL 30s
3. 吊销传播:
- 吊销事件写入总线后1~5 秒内刷新缓存。
- 验收阈值:吊销生效延迟 `<= 5s`
## 5. 错误语义
| 场景 | HTTP | error.code | 说明 |
|---|---|---|---|
| 缺失 Bearer | 401 | AUTH_MISSING_BEARER | 请求头缺失 |
| query key 外部入站 | 401 | QUERY_KEY_NOT_ALLOWED | 边界拒绝 |
| token 无效/签名失败 | 401 | AUTH_INVALID_TOKEN | 校验失败 |
| token 已吊销/过期 | 401 | AUTH_TOKEN_INACTIVE | 状态不可用 |
| scope 不足 | 403 | AUTH_SCOPE_DENIED | 权限不足 |
## 6. 审计事件TOK-004 依赖)
1. `token.authn.success`
2. `token.authn.fail`
3. `token.authz.denied`
4. `token.query_key.rejected`
最小字段:
1. `event_id`
2. `request_id`
3. `token_id`(可空,提取失败时为空)
4. `subject_id`(可空)
5. `route`
6. `result_code`
7. `client_ip`
8. `created_at`
## 7. 伪代码(实现参考)
```text
onRequest(req):
reqId = ensureRequestId(req)
if hasExternalQueryKey(req):
emitAudit("token.query_key.rejected", reqId, route, clientIp)
return 401 QUERY_KEY_NOT_ALLOWED
bearer = parseBearer(req.headers.Authorization)
if bearer is null:
emitAudit("token.authn.fail", reqId, route, "AUTH_MISSING_BEARER")
return 401 AUTH_MISSING_BEARER
claims = verifyToken(bearer)
if verify failed:
emitAudit("token.authn.fail", reqId, route, "AUTH_INVALID_TOKEN")
return 401 AUTH_INVALID_TOKEN
status = getTokenStatus(claims.jti)
if status != active:
emitAudit("token.authn.fail", reqId, route, "AUTH_TOKEN_INACTIVE")
return 401 AUTH_TOKEN_INACTIVE
if !checkScopeRole(claims.scope, claims.role, route):
emitAudit("token.authz.denied", reqId, route, "AUTH_SCOPE_DENIED")
return 403 AUTH_SCOPE_DENIED
attachPrincipal(req, claims)
emitAudit("token.authn.success", reqId, route, "OK")
pass
```
## 8. 开发阶段验收(设计级)
1.`TOK-001` 角色、状态机、审计字段一致。
2.`M-014/M-016` 指标定义一致。
3. 与 OpenAPI token 契约草案字段一致。