Skip to content

认证与授权

本章导读:无状态约束把"证明你是谁"压到了每个请求上,HTTP 为此提供了一整套标准化机制(RFC 7235 认证框架 + 注册方案)。本章讲清 Authorization/401/WWW-Authenticate 的协议契约,对比 Basic / Bearer(JWT) / OAuth 2.0 / API Key 四种方案的适用边界,并给出会话与令牌生命周期设计。

1. 协议层契约(RFC 7235 / RFC 9110 §11)

挑战-应答模型:

客户端 → GET /articles/42                 (无凭证)
服务器 → 401 Unauthorized
         WWW-Authenticate: Bearer realm="api", error="invalid_token",
                           error_description="令牌已过期"
客户端 → GET /articles/42
         Authorization: Bearer eyJhbGciOi…
服务器 → 200 OK

规范要点:

  • Authorization: <scheme> <credentials>——scheme 大小写不敏感,但惯用 Bearer/Basic
  • 401 响应必须携带 WWW-Authenticate(RFC 7235 MUST);
  • 403401 分工:认证失败 401,认证通过但无权 403;
  • 认证头部没有统一标准时不要自创裸头部(X-Auth: … 违反自描述原则),用注册 scheme 或文档化的 Authorization 扩展。

2. 四种主流方案

2.1 API Key(最简单)

GET /v1/articles?key=sk-live-3f9a…          ← 查询参数(不推荐:进日志/Referer)
GET /v1/articles
X-Api-Key: sk-live-3f9a…                    ← 惯用头部(虽然 X- 已不推荐,但生态既成事实)
Authorization: ApiKey sk-live-3f9a…         ← IANA 较新登记的 ApiKey 认证方案(客户端支持度先验证)

适合:服务间调用、低频开放接口。身份粒度=一个 key 一个主体,无用户级权限模型。key 必须:仅 HTTPS、可吊销、可轮换、按环境隔离、限 scopes。

2.2 Basic(RFC 7617)

Authorization: Basic base64(user:pass)——不是加密是编码,仅限 HTTPS + 引导脚本/内网工具;现代用户端 API 基本淘汰,OAuth 的 client credentials 流程内部仍会用到。

2.3 Bearer Token(RFC 6750 + JWT RFC 7519)

http
POST /v1/auth/login
{ "username": "ma_gua", "password": "……" }

200 OK
{
  "accessToken":  "eyJhbGciOiJSUzI1NiIs…",    短时(15min)
  "tokenType":    "Bearer",
  "refreshToken": "dGhpcyBpcyBhIHJlZnJl…",    长时(30d),仅用于换新
  "expiresIn":    900
}

GET /v1/articles/42
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…

JWT 三段式(header.payload.signature)携带声明(claims):

声明含义校验点
iss签发者白名单
sub主体(用户 ID)
aud受众(哪个 API 能用)必须校验,防跨服务挪用
exp/iat/nbf时间窗时钟偏移容差 ≤60s
scope/roles权限授权依据
jti令牌唯一 ID吊销黑名单的锚点

取舍:JWT 自包含 vs 不透明令牌(Opaque Token + introspection)

JWT不透明令牌(如 at_8f3d…
校验成本本地验签,零回源每请求问认证服务(或缓存)
即时吊销难(需黑名单)易(存活性即吊销)
体积大(数百字节,每请求都带)
泄露面payload 可见敏感声明无内容可偷看

经验:高 QPS、跨服务、可接受"短 TTL + 黑名单兜底"→ JWT;强合规、需秒级踢人 → 不透明令牌。

2.4 OAuth 2.0 / 2.1(RFC 6749 家族)

四个角色的委托授权框架——"第三方 App 访问我的文章,但不给我的密码":

授权码流程(Authorization Code + PKCE,2026 年唯一推荐的用户态流程):
1 App →  授权服务器: /authorize?response_type=code&code_challenge=…(浏览器登录)
2 用户同意 → 回调 App: ?code=…
3 App →  授权服务器: POST /token  code + code_verifier   → access_token + refresh_token
4 App →  资源服务器(API): Authorization: Bearer <access_token>

要点:

  • 机密客户端(后端服务):授权码 + client_secret,或 client_credentials(服务间);
  • 公共客户端(SPA/移动):必须 PKCE(RFC 7636);
  • OAuth 管"授权"(scope),不管"认证"——拿 OIDC(OpenID Connect)做登录身份;
  • 细粒度持续授权演进方向:FGA(关系型权限,如 OpenFGA/Zanzibar 模型)、UMA(基于 OAuth 的用户管理授权)——REST 侧以 403 + problem type 表达即可。

3. 会话资源的 REST 建模

"登录态"其实可以名词化:

http
POST   /v1/sessions              ← 登录:创建会话资源 → 201 + Location: /v1/sessions/cur
GET    /v1/sessions/cur          ← 当前会话(/me 的同款思路)
DELETE /v1/sessions/cur          ← 登出:销毁会话 → 204
DELETE /v1/sessions/{id}         ← 管理员踢线
GET    /v1/users/me/sessions     ← 我的设备列表(每行带 revoke 链接)

收益:登出/踢线/设备管理全部资源化,审计与权限模型一致;refresh token 的撤销即 DELETE /v1/sessions/{id}

4. 授权:403 的正确姿势

http
PATCH /v1/articles/42
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/permission-denied",
  "title": "无权编辑该文章",
  "status": 403,
  "detail": "需要 role=author 或 editor",
  "requiredScopes": ["articles:write"]
}
  • 模型选择:RBAC(角色)→ ABAC(属性策略)→ ReBAC(关系,Zanzibar 系);API 层表现一致:缺什么声明清楚(防客户端瞎猜),必要时给"申请权限"的链接(HATEOAS 式 rel="request-access");
  • 未认证访问私有资源返回 401;已认证无权限返回 403;防枚举可统一 404——三选一策略写进规范并全局一致;
  • 权限校验放统一拦截层(网关/中间件声明式规则)+ 资源级细校验(如"只能改自己的草稿")。

5. 安全基线清单

  • [ ] 全站 HTTPS(HSTS:Strict-Transport-Security: max-age=31536000; includeSubDomains
  • [ ] 令牌:access 短 TTL(≤15min)、refresh 旋转(rotation)+ 重用检测
  • [ ] JWT 验签:算法固定白名单(拒绝 alg: none 与算法混淆攻击)、校验 iss/aud/exp
  • [ ] 密钥托管 KMS/Secrets Manager + 定期轮换(JWKS 端点支持 kid 双活轮换)
  • [ ] 登录接口防暴力:限流 + 验证码 + 失败锁定;凭据永不回显
  • [ ] 敏感操作二次认证(step-up:401 + WWW-Authenticate: Bearer step_up="…") 或 MFA 资源建模
  • [ ] 令牌不写 URL(查询参数会进日志/Referer/历史记录);Cookie 场景 Secure/HttpOnly/SameSite=Lax|Strict

6. 本章小结

  • 协议层:Authorization + 401/WWW-Authenticate 是框架,方案(Basic/Bearer/ApiKey)可插拔。
  • Bearer+JWT 是 API 认证默认解;OAuth 2.0 解决第三方委托;两者是"框架与流程"关系不是竞争关系。
  • 把"会话/令牌"建模为资源(POST /sessionsDELETE /sessions/{id}),登出、踢线、设备管理一色 REST。
  • 授权失败的 problem 响应要携带"缺什么",让客户端可自助修复。

7. 下一步

批量怎么写?部分成功怎么办? → 批量操作设计