3. API Key 认证:API Key 的生成与分发、在 OpenAPI 中定义 API Key、Header/Query/Cookie 三种传递方式
API Key(应用程序编程接口密钥)是服务端为每个合法调用方分配的唯一标识字符串,用于识别调用方身份并控制资源访问。它是 RESTful API 中最简单、最轻量的认证方式,适合服务端到服务端、微服务间或开放平台场景。
3.1 API Key 的生成与分发
一个安全的 API Key 需要满足 不可预测性、唯一性 和 可撤销性。生成过程通常包含以下步骤:
- 熵源采集:使用系统级随机数生成器(如
/dev/urandom、SecureRandom)采集足够熵值。 - 编码转换:将二进制随机字节编码为可打印字符串,常用 Base64 URL-safe 或 Hex 编码。
- 前缀标识:添加可读前缀(如
sk_live_、pk_test_)便于区分环境与类型。 - 哈希存储:服务端仅存储 API Key 的 SHA-256 哈希值,原始 Key 只在生成时一次性展示给用户。
安全实践: 推荐 Key 长度 ≥ 32 字节(256 位),使用
crypto/rand(Go)、secrets.token_urlsafe()(Python)或 java.security.SecureRandom 生成。禁止使用 UUID v4 直接作为 API Key,因其随机性不足且可被预测。
分发流程通常包含注册、生成、展示、确认四个阶段:
| 阶段 | 动作 | 安全要求 |
|---|---|---|
| 注册 | 用户提交身份信息,完成实名或邮箱验证 | 验证用户身份真实性 |
| 生成 | 服务端生成 Key,记录哈希值到数据库 | 使用安全随机数,不记录明文 |
| 展示 | 在控制台或响应中一次性展示原始 Key | 仅展示一次,页面提示复制保存 |
| 确认 | 用户复制 Key 后,服务端不再提供明文查看 | 支持重新生成(使旧 Key 失效) |
3.2 在 OpenAPI 中定义 API Key
OpenAPI 3.x 使用 securitySchemes 组件定义 API Key 认证方案,再通过 security 字段应用到全局或具体操作。核心结构如下:
openapi: 3.0.3
info:
title: 支付网关 API
version: 1.0.0
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: 在请求头中传递 API Key,格式为 X-API-Key: <your_key>
security:
- ApiKeyAuth: []
paths:
/orders:
get:
summary: 获取订单列表
security:
- ApiKeyAuth: []
responses:
'200':
description: 成功返回订单列表
关键字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
type |
string | 固定为 apiKey |
in |
string | 传递位置:header、query 或 cookie |
name |
string | 参数名称,如 X-API-Key、api_key、session_token |
description |
string | 对调用方的使用说明(可选但推荐) |
最佳实践: 在
components/securitySchemes 中定义一次,然后在 security 中引用。如果某些端点不需要认证,可在该端点层级设置 security: [] 覆盖全局配置。
3.3 Header / Query / Cookie 三种传递方式
API Key 可以通过三种位置传递给服务端,每种方式在安全性、缓存友好性和使用场景上各有差异。
3.3.1 Header 传递(推荐)
将 API Key 放在自定义 HTTP 请求头中,是最常用且安全的方式。Key 不会出现在 URL 中,避免被代理服务器、浏览器历史或 Referer 头泄露。
# 请求示例
GET /api/v1/orders HTTP/1.1
Host: api.example.com
X-API-Key: sk_live_3a1b2c3d4e5f67890abcdef123456789
Content-Type: application/json
OpenAPI 定义:
components:
securitySchemes:
HeaderApiKey:
type: apiKey
in: header
name: X-API-Key
注意: 避免使用标准认证头
Authorization: Bearer <token> 来传递 API Key,这会造成语义混淆。API Key 应使用自定义头,如 X-API-Key、X-Api-Key 或 Api-Key。
3.3.2 Query 参数传递
将 API Key 作为 URL 查询参数传递,适合临时调试、WebSocket 连接或无法修改请求头的场景。但安全性较低,Key 会出现在服务器日志、浏览器历史、Referer 头中。
# 请求示例
GET /api/v1/orders?api_key=sk_live_3a1b2c3d4e5f67890abcdef123456789 HTTP/1.1
Host: api.example.com
OpenAPI 定义:
components:
securitySchemes:
QueryApiKey:
type: apiKey
in: query
name: api_key
安全警告: 生产环境禁止使用 Query 参数传递 API Key。URL 会被完整记录在 CDN、反向代理、Web 服务器访问日志中,导致 Key 长期泄露。仅限开发调试或一次性使用场景。
3.3.3 Cookie 传递
将 API Key 放在 Cookie 中,适合浏览器端应用自动携带凭证。但 Cookie 存在 CSRF 攻击风险,且跨域场景下需要额外配置 SameSite 和 Secure 属性。
# 请求示例
GET /api/v1/orders HTTP/1.1
Host: api.example.com
Cookie: session_token=sk_live_3a1b2c3d4e5f67890abcdef123456789
OpenAPI 定义:
components:
securitySchemes:
CookieApiKey:
type: apiKey
in: cookie
name: session_token
Cookie 使用建议: 设置
HttpOnly 防止 JavaScript 读取,设置 Secure 仅通过 HTTPS 传输,设置 SameSite=Lax 或 Strict 缓解 CSRF。如果 API 被非浏览器客户端调用,优先选择 Header 方式。
3.4 三种传递方式对比总结
| 对比维度 | Header | Query | Cookie |
|---|---|---|---|
| 安全性 | 高(不进入 URL 日志) | 低(URL 日志泄露) | 中(需防范 CSRF) |
| 缓存友好性 | 高(Header 不参与缓存键) | 低(不同 Key 产生不同缓存) | 高(Cookie 不参与缓存键) |
| 浏览器自动携带 | 需手动设置 | 需手动拼接 | 自动携带 |
| 跨域支持 | 需配置 CORS 白名单头 | 天然支持 | 需配置 CORS + withCredentials |
| OpenAPI in 值 | header |
query |
cookie |
| 推荐场景 | 服务端到服务端、移动端 API | 仅开发调试 | 浏览器端 Web 应用 |
3.5 多 API Key 方案与作用域
一个 OpenAPI 规范可以定义多个 API Key 认证方案,用于不同用途:
components:
securitySchemes:
ReadOnlyKey:
type: apiKey
in: header
name: X-Read-Key
description: 只读 API Key,仅允许 GET 请求
WriteKey:
type: apiKey
in: header
name: X-Write-Key
description: 读写 API Key,允许 POST/PUT/DELETE
security:
- ReadOnlyKey: []
paths:
/orders:
get:
security:
- ReadOnlyKey: []
- WriteKey: []
post:
security:
- WriteKey: []
作用域设计: 可以为不同 API Key 分配不同权限范围(如只读、读写、管理),在服务端验证 Key 时检查其关联的作用域。OpenAPI 3.1 支持
securitySchemes.flows 扩展,但 API Key 本身不原生支持 OAuth2 的作用域机制,需在业务逻辑中自行实现。