主题
07 · 鉴权 Security:Token / OAuth2 / API Key 全集
生活类比:鉴权就是进小区/楼/办公室的门禁系统——
apiKey= 门禁卡(身份卡,刷一下就过)http basic= 跟保安报姓名 + 密码bearer (JWT)= 自带 GPS 的智能手环(卡里写着你身份 + 权限 + 失效时间)OAuth2= "用微信登录" 的扫码授权OpenID Connect= OAuth2 的升级版(除了授权还能告诉你"这人是谁")
1. 鉴权在 OpenAPI 里怎么写:两步
步骤 1:在 components.securitySchemes 里定义鉴权方案
yaml
components:
securitySchemes:
bearerAuth: # ← 你给方案起的名字
type: http
scheme: bearer
bearerFormat: JWT步骤 2:在全局或单个接口上应用
yaml
# 全局应用(所有接口默认要鉴权)
security:
- bearerAuth: [] # 引用上面定义的名字
paths:
/login:
post:
security: [] # 单个接口可以覆盖:登录免鉴权
...
/users:
get: # 没写 security,继承全局
...数组里
[]是 OAuth2 的 scope 列表,非 OAuth2 方案就空数组。
2. 五种 securitySchemes 一览
type | 子类型 | 适用场景 |
|---|---|---|
apiKey | in: header / query / cookie | 内部系统、简单 SaaS |
http | scheme: basic | 老接口、内网管理后台 |
http | scheme: bearer | 现代 REST 主流(JWT / opaque token) |
oauth2 | 4 种 flow | 第三方登录(GitHub / 微信 / Google) |
openIdConnect | - | OAuth2 + 标准用户信息(OIDC) |
mutualTLS (3.1) | - | 高安全的 B2B 接口 |
3. apiKey · 最简单的钥匙
3.1 放在 header(最常见)
yaml
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key # 头的名字请求示例:
GET /users HTTP/1.1
X-API-Key: sk_live_abc123def4563.2 放在 query(不推荐,会被 access log 记录)
yaml
ApiKeyAuth:
type: apiKey
in: query
name: api_keyGET /users?api_key=sk_live_abc1233.3 放在 cookie
yaml
SessionAuth:
type: apiKey
in: cookie
name: SESSIONID实际工程:90% 用 header,简单清晰。
4. http basic · 古老但还活着
yaml
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic请求里:
GET /admin HTTP/1.1
Authorization: Basic YWxpY2U6cGFzc3dvcmQ=
↑ base64('alice:password')Base64 不是加密!抓包就能解出明文。只能配合 HTTPS 用。 实际场景:内网管理工具、Nginx 临时保护页。
5. http bearer · 现代主流
yaml
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT # 或 opaque、PASETO 等(仅描述用,不影响验证)请求里:
GET /me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiI.eyJzdWIiOiIxMjM0NSJ9.signature5.1 JWT 长啥样?
header.payload.signature
| | |
| | └── 服务端用密钥签名,防篡改
| └────────── base64({"sub": "user-123", "exp": 1700000000, "role": "admin"})
└────────────────── base64({"alg": "HS256", "typ": "JWT"})把整段贴到 https://jwt.io 能直接解出来看(payload 是明文 base64!别在里面塞密码)。
5.2 完整示例
yaml
openapi: 3.0.3
info: { title: 带鉴权的 API, version: 1.0.0 }
security: # ← 全局默认要 bearer
- bearerAuth: []
paths:
/login:
post:
security: [] # 登录免鉴权
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
username: { type: string }
password: { type: string, format: password }
responses:
'200':
description: 登录成功,返回 JWT
content:
application/json:
schema:
type: object
properties:
token: { type: string }
/me:
get:
summary: 获取当前用户信息
responses:
'200': { description: OK }
'401': { description: 未登录或 token 过期 }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT在 Swagger UI 上:右上角会出现 🔒 Authorize 按钮,点开输入 token,之后所有 Try it out 都自动带上 Authorization: Bearer xxx。
6. OAuth2 · 第三方授权
6.1 4 种 Flow(授权流程)
| Flow | 谁用 | 现状 |
|---|---|---|
authorizationCode | Web 应用(带后端) | ✅ 推荐 |
implicit | 纯前端 SPA | ⚠️ 已废弃,改用 PKCE |
password | 第一方应用(自己 App 自己登录) | ⚠️ 不推荐了 |
clientCredentials | 服务到服务(M2M) | ✅ 后端 API 调用 |
6.2 完整 OAuth2 schema(最常见的 authorizationCode)
yaml
components:
securitySchemes:
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/oauth/authorize
tokenUrl: https://example.com/oauth/token
refreshUrl: https://example.com/oauth/refresh
scopes:
read:user: 读取用户信息
write:user: 修改用户信息
read:order: 读取订单
write:order: 创建订单6.3 在接口上用 scope
yaml
paths:
/users/me:
get:
security:
- OAuth2: [read:user] # ← 这个接口需要 read:user 权限
/orders:
post:
security:
- OAuth2: [write:order] # 需要 write:order6.4 OAuth2 流程图
1. 用户点 "用 GitHub 登录"
↓
2. 浏览器跳转到 GitHub 的 authorizationUrl
GET https://github.com/login/oauth/authorize?client_id=xxx&scope=read:user&redirect_uri=...
↓
3. 用户在 GitHub 上同意授权
↓
4. GitHub 回跳到你的 redirect_uri,带上 code
GET https://yourapp.com/callback?code=abcd
↓
5. 你的后端用 code 去 tokenUrl 换 access_token
POST https://github.com/login/oauth/access_token
↓
6. 拿到 token 后,用 Authorization: Bearer xxx 调 GitHub API7. OpenID Connect
OIDC = OAuth2 + 标准化的 userinfo 接口 + ID Token。
yaml
components:
securitySchemes:
OIDC:
type: openIdConnect
openIdConnectUrl: https://accounts.google.com/.well-known/openid-configuration一行配置,工具自动从 well-known URL 拉取所有 endpoint 和支持的 scope。Google / Auth0 / Keycloak 都用这套。
8. 多种鉴权同时存在 · 组合规则
8.1 二选一("或")
yaml
security:
- bearerAuth: []
- apiKeyAuth: []→ "带 Bearer Token 或 API Key 任意一种"。
8.2 同时满足("与")
yaml
security:
- bearerAuth: []
apiKeyAuth: []→ "Bearer 和 API Key 都要带"(看仔细,少一个 -)。
8.3 完全不需要鉴权
yaml
security: []9. 实战:典型混合鉴权 yaml
yaml
openapi: 3.0.3
info: { title: 混合鉴权示例, version: 1.0.0 }
security:
- bearerAuth: [] # 默认要 JWT
paths:
/healthz:
get:
security: [] # 健康检查免鉴权
responses: { '200': { description: pong } }
/users:
get:
security:
- bearerAuth: []
- apiKeyAuth: [] # 二选一:用户 token 或 API key
responses: { '200': { description: OK } }
/admin/reset:
post:
security:
- bearerAuth: []
apiKeyAuth: [] # 双重鉴权("与")
responses: { '200': { description: OK } }
/external/sync:
post:
security:
- OAuth2: [write:order, write:user] # OAuth2 多 scope
responses: { '200': { description: OK } }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKeyAuth:
type: apiKey
in: header
name: X-API-Key
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://oauth.example.com/authorize
tokenUrl: https://oauth.example.com/token
scopes:
read:user: 读用户
write:user: 写用户
read:order: 读订单
write:order: 写订单10. Swagger UI 的 "Authorize" 按钮
写完 securitySchemes,UI 右上角自动出现 🔒 Authorize 按钮:
┌────────────────── Available authorizations ──────────────────┐
│ │
│ bearerAuth (http, Bearer) │
│ Value: [_________________________] [Authorize] │
│ │
│ apiKeyAuth (apiKey, header: X-API-Key) │
│ Value: [_________________________] [Authorize] │
│ │
└──────────────────────────────────────────────────────────────┘输入一次,所有接口的 Try it out 都自动带上对应的 header。调试神器。
11. 常见坑
11.1 把 token 放进了 yaml 里
❌ 千万不要!
yaml
# 巨坑!别把真 token 写进 yaml,提交 Git 会泄漏
security:
- bearerAuth: ['eyJhbGciOiJIUzI1NiI...']security 数组里的内容是 scope 名字,不是 token!
11.2 Authorization 头自己写在 parameters
❌ 会冲突:
yaml
parameters:
- name: Authorization # 错!
in: header
schema: { type: string }应该用 securitySchemes。
11.3 bearerFormat 误以为是验证逻辑
bearerFormat 只是给文档读者看的提示("我这是 JWT"),不会被工具拿来验证 token。验证逻辑在后端。
12. 章末面试题速览
- JWT 的 payload 是加密的吗? → 不是!只是 base64 编码,谁拿到都能解。所以别把密码、隐私信息写进 payload。
- OAuth2 的 implicit flow 为什么被废弃? → token 走 URL fragment 返回,浏览器历史 / 日志容易泄漏;现在 SPA 推荐
authorizationCode + PKCE。 - HTTP Basic 跟 Bearer 的区别? → Basic 用"用户名 + 密码"明文 base64 每次都传;Bearer 用 token,登录一次后一直用,泄漏了至少能 revoke。
🎬 可视化演示
下方 demo 是一个 "鉴权方案选择器"——选不同方案,看到对应的 yaml 和模拟 HTTP 请求。
🎬 可视化演示
演示加载缓慢或样式异常?点此在新标签页打开 ↗