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

3.8 KiB
Raw Permalink Blame History

平台鉴权与 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
    • 校验签名、issaudexpnbfjti
    • 签名失败返回 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_cacheTTL 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. 伪代码(实现参考)

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 契约草案字段一致。