API 安全与访问治理:从身份到对象级授权
0. 元信息
- 主题路径:
docs/topics/rest-api-design/subtopics/security-governance/README.md - 父主题:
rest-api-design - 主分类:Web 与后端
- 辅助分类:工程技术
- 适合对象:会写 HTTP handler、SQL 与自动化测试的后端开发者
- 建议周期:1~2 周(每周 6~8 小时)
- 前置知识:
resource-http-contracts - 最终目标:覆盖 OAuth2/JWT、RBAC/ABAC、对象授权、CORS、rate-limit 与 quota
1. 学习路线
威胁模型 → TLS → OAuth2/JWT → scope → 对象授权 → CORS → rate-limit/quota
2. 阶段周数分配
精简子主题不固定周数。按 §3 顺序完成,卡住时回到 RFC、契约样例和可重复请求。
3. 九阶段表
| 阶段 | 核心知识 | 实践产出 | 学会标准 |
|---|---|---|---|
| 1 | 术语与边界 | 术语表 | 不混淆相邻概念 |
| 2 | HTTP/标准语义 | 请求矩阵 | 能引用规范解释 |
| 3 | 数据与状态 | schema/状态图 | 正常与冲突可区分 |
| 4 | 安全边界 | 威胁清单 | 非法路径被拒绝 |
| 5 | 失败语义 | 错误目录 | 客户端能稳定处理 |
| 6 | 兼容策略 | 变更表 | 能识别 breaking change |
| 7 | 工具验证 | curl/collection | 可重复运行 |
| 8 | 自动检查 | 契约测试 | 修改后会报警 |
| 9 | 小项目 | 可运行交付 | 第三方可复现 |
4. 第一周任务
精简版省略固定日程。做一个最小正常请求,再补认证失败、非法输入、冲突、重试与超时。
5. 阶段通用验收
精简版省略;每个产出至少保留命令、预期、实际与版本。
6. 最终验收
精简版省略;以 §3 第 9 阶段和 §9.4 问题口述检查为准。
7. 综合项目
精简版省略;成果并入父主题电商商品 / 订单 API。
本主题贡献
- 在父主题
rest-api-design的综合项目「电商商品 / 订单 API」中,本主题(API 安全与访问治理)负责 OAuth2 Authorization Code + PKCE、对象级授权、CORS、rate-limit / quota:把 §1 路线「威胁模型 → TLS → OAuth2/JWT → scope → 对象授权 → CORS → rate-limit/quota」落到/oauth/authorize、/oauth/token、/api/orders/{id}的具体端点。 - 工程动作:用 OpenSSL 生成 PKCE
code_verifier + code_challenge (S256)+state + nonce,完成浏览器跳/authorize→302 redirect_uri?code&state→ POST/token的完整 Authorization Code + PKCE redirect 流;用 RS256 签名 JWT 与 JWKS endpoint;用 scope =orders.read/orders.write与对象授权(order.owner_id == sub)双层校验防 IDOR;用 CORS 白名单 + preflight 演练;用 Redis Lua 令牌桶(key =sub + path)做网关粗 / 进程内细两层限流。 - 与 rest-api-design 其它子主题对接:把
securitySchemes入口加到 resource-http-contracts 的 OpenAPI yaml;为 performance-delivery 提供X-RateLimit-Remaining / Retry-After响应头;给 openapi-operations 提供脱敏日志(不记 token / code / verifier,只记sub / jti / trace)与 traceid 串联。
交付物清单:
security/oauth_pkce.sh:Authorization Code + PKCE 完整可重跑脚本,含code_verifier / code_challenge / state / nonce生成与redirect_uri精确匹配、错误用例(state 校验失败、code 已用、verifier 错误);security/jwt_rs256.{py|go}:RS256 签名与校验 + JWKS endpoint + 短寿命 access_token(≤ 15 min)+ refresh_token rotation(单次使用 + 黑名单)校验;security/authz.md:≥ 5 条对象授权用例(含 IDOR 攻击复现与修复),证明读 / 写都走order.owner_id == sub+ scope 双层校验;security/ratelimit.{lua|py}+cors.md:Redis Lua 令牌桶(key =sub + path)+ CORS 白名单 + preflight 演练 + 429 携带Retry-After的压测报告。
验收标准:
- PKCE 脚本在 SPA 回调后能换出 token,access_token ≤ 15 min,且过期 token 被拒绝(401 +
WWW-Authenticate); - IDOR 复现:未登录 / 他用户用别人 order id 直接
200 OK→ 修复后403 Forbidden;≥ 5 条用例全部通过; - 限流:同 sub 在网关限 100 r/s、进程内限 20 r/s 时 429
Retry-After携带正确秒数,且刷新后允许继续; - 撤 token:refresh_token 单次使用 + rotation 校验,黑名单命中 ≤ 50 ms 完成拒绝;
- CORS:白名单外 Origin 的 preflight 在网关层直接
403,OPTIONS 不会打到业务。
8. 推荐资料
精简版省略;使用 §9.3 和 §9.6 Source。
9. 学习资料汇聚(v0.3 自包含)
9.1 背景与动机
覆盖 OAuth2/JWT、RBAC/ABAC、对象授权、CORS、rate-limit 与 quota。API 一旦被客户端依赖,错误的语义会变成多年兼容成本。我们用规范、可运行样例和失败测试约束选择。
9.2 概念地图
flowchart LR
Design[设计] --> Contract[HTTP/OpenAPI 契约]
Contract --> Runtime[实现/网关]
Runtime --> Client[客户端]
Runtime --> Evidence[日志/指标/trace]
Test[契约与失败测试] -.验证.-> Contract
Test -.验证.-> Runtime
这张图把设计、实现、调用和证据串起来。任何规则都要能落到契约或运行时检查。
9.3 基础知识讲解
9.3.1 论文 / 规范
- Roy Fielding, Architectural Styles and the Design of Network-Based Software Architectures:REST 起源。
- RFC 9110, HTTP Semantics:方法、状态码、缓存语义。
- RFC 7807, Problem Details for HTTP APIs:统一错误对象。
9.3.2 书
- Leonard Richardson, Mike Amundsen, Sam Ruby, RESTful Web APIs。
- Arnaud Lauret, The Design of Web APIs。
- JJ Geewax, API Design Patterns。
9.3.3 博客 / 文档
- Stripe API 文档:idempotency、分页、错误风格。
- Microsoft REST API Gui:兼容与一致性。
- Google API Design Guide:资源导向设计。
- Apigee 文档 与 AWS API Gateway 文档:网关、配额、部署。
9.3.4 人物
- Roy Fielding:REST 与 HTTP 架构。
- Tim Berners-Lee:Web、URI、HTTP。
- Darrel Miller:OpenAPI Initiative 技术治理与工具生态。
9.3.5 方法
- Contract-first:先写 OpenAPI 和示例,再写 handler。
- Consumer-driven review:从调用方任务检查命名、错误和兼容。
- Single-variable test:一次只改变 method、header、身份或并发量。
- Compatibility budget:发布前列出 breaking change 与迁移期。
9.4 经典问题与经典案例
| 问题 | 为什么重要 | 最简答案 |
|---|---|---|
| JWT 是否等于授权 | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
| 401 与 403 怎么返回 | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
| 如何防 IDOR | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
| CORS 能否保护服务端 | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
| rate-limit key 怎么选 | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
| token 如何撤销 | 影响客户端正确性和长期兼容 | 写清语义、失败码、重试与测试证据 |
9.4.1 实战片段:OAuth2 Authorization Code + PKCE 完整 redirect 流
角色:浏览器(User-Agent)、SPA 客户端(client_id=spa-app)、
授权服务器(https://auth.example.com)、资源服务器(https://api.example.com)
1) 客户端生成 verifier 与 challenge
code_verifier = random_base64url(32..96) # 例:dBjftJeZ4CVP...B6M
code_challenge = base64url(sha256(code_verifier))
code_challenge_method = S256
state = random_base64url(16) # 防 CSRF
nonce = random_base64url(16) # 防 replay
GET https://auth.example.com/authorize
?response_type=code
&client_id=spa-app
&redirect_uri=https://app.example.com/cb
&scope=openid%20profile%20orders.read%20orders.write
&state=g5Kq9...n2
&nonce=H2x4...8
&code_challenge=E9Melhoa2OwvFr...Fw
&code_challenge_method=S256 HTTP/1.1
HTTP/1.1 302 Found
Location: https://app.example.com/cb
?code=SplxlOBeZQQYbYS6WxSbIA
&state=g5Kq9...n2
POST https://auth.example.com/token
Content-Type: application/x-www-form-urlencoded
Accept: application/json
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://app.example.com/cb
&client_id=spa-app
&code_verifier=dBjftJeZ4CVP...B6M
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"id_token": "eyJhbGciOiJSUzI1NiIs...",
"scope": "openid profile orders.read orders.write"
}
GET https://api.example.com/orders HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Idempotency-Key: 9f2b6c1a-1f3d-4b8e-9b6c-2a7f0b5c91a2
X-Request-Id: 6a1d2c4b-...
# 1. 本地一次性生成 PKCE pair
verifier=$(openssl rand -base64 64 | tr -d '=+/' | cut -c1-64)
challenge=$(printf %s "$verifier" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')
state=$(openssl rand -hex 16)
# 2. 浏览器跳到授权页(PKCE 公共客户端无需 client_secret)
open "https://auth.example.com/authorize?response_type=code&client_id=spa-app&redirect_uri=https://app.example.com/cb&scope=openid%20profile%20orders.read&state=$state&code_challenge=$challenge&code_challenge_method=S256"
# 3. 回调拿到 code 后换 token
curl -s https://auth.example.com/token \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://app.example.com/cb \
-d client_id=spa-app \
-d code_verifier="$verifier"
# 4. 对象授权检查示例(伪代码)
# 读 orders/{id} 前必须满足:order.owner_id == sub 且 orders.read 在 scope 内
# 验证清单(落地对照)
- 公共客户端只走 PKCE,不下发 client_secret
- redirect_uri 精确匹配(不接受通配子域)
- state 与 nonce 在回调里校验
- access_token 短寿命(<=15 min),refresh_token 单次使用 + rotation
- 后端每次请求做对象级授权,不能只靠 scope
- 撤销:refresh token 存哈希 + 黑名单;access token 接受短窗口失效
- 日志不记录 token、code、verifier;只记 sub/jti/trace
9.5 学习难点
9.5 学习难点
- 概念难点:规范术语相近。卡点来自用框架默认值代替 HTTP 语义;回 RFC 和 wire example。
- 思维难点:正常流很短,重试、并发、权限和演进才难;先列状态机与不变量。
- 工程难点:网关、应用、数据库和客户端行为不同;用端到端请求、契约测试和 trace 定位边界。
9.6 技术标准与接口
9.6.1 Entity
| 名称 | 版本 | 组织 | 状态 / 可访问性 |
|---|---|---|---|
| OpenAPI Specification | 3.1 | OpenAPI Initiative | 正式;Apache-2.0 仓库,规范公开 |
| JSON:API | 1.1 | JSON:API 社区 | 正式;公开 |
| JSON Schema | 2020-12 | JSON Schema / IETF 社区 | 正式;公开 |
| Problem Details for HTTP APIs | RFC 7807 | IETF | 已被 RFC 9457 更新;RFC 公开 |
| HAL | draft-kelly-json-hal | IETF Internet-Draft / HAL 社区 | 草案;公开 |
9.6.2 Scope
OpenAPI 描述 HTTP API;JSON Schema 描述数据约束;JSON:API 与 HAL 约定表示和链接;Problem Details 约定机器可读错误。它们不替代业务授权、事务设计或运行时保护。
9.6.3 Structure
OpenAPI 必会 paths、operations、parameters、requestBody、responses、components、securitySchemes;JSON Schema 必会 type、required、properties、组合与引用;Problem Details 必会 type/title/status/detail/instance。
9.6.4 Ecosystem
实现与工具包括 Swagger UI、Redoc、Spectral、OpenAPI Generator、Postman、Insomnia、Bruno、httpie、curl。API gateway 常用 Apigee、AWS API Gateway、Kong、Envoy。工具默认值不是规范。
9.6.5 Depth Tiers
| 层级 | 可观察能力 |
|---|---|
| L0 | 知道这些标准解决什么问题 |
| L1 | 看懂 schema、operation 和 problem body |
| L2 | 能正确编写、校验、生成客户端并调用 |
| L3 | 能解释语义、排错兼容/鉴权/缓存/限流问题 |
| L4 | 能设计扩展、治理规则与跨团队演进机制 |
本计划目标:L3。
9.6.6 Source
- OpenAPI Initiative 仓库:OpenAPI 3.1 原文与版本历史。
- IETF RFC Editor:RFC 9110、7807/9457、6585 等原文。
- JSON Schema 2020-12 与 JSON:API 1.1。
- 引用快照:2026-07-30。实施前检查更新、勘误与废止关系。
10. 常见误区
- 用框架默认值代替明确契约
- 只测 2xx
- 错误 body 无稳定 code
- 混淆认证与授权
- 重试无 idempotency
- 缓存 key 漏身份/查询条件
- 版本化代替兼容设计
- 文档与实现分开维护
- 日志泄露 secret
- 只有 dashboard,没有可定位的 trace。
11. 所有知识点分类(统一规则)
- 编程语言 2. 数据结构与算法 3. 计算机基础 4. 工程技术 5. Web 与后端 6. 前端与客户端 7. 数据与人工智能 8. 项目与职业能力 9. 安全与可靠性
本计划归属:Web 与后端 主 + 工程技术 辅。