3. API Key 认证:API Key 的生成与分发、在 OpenAPI 中定义 API Key、Header/Query/Cookie 三种传递方式

API Key(应用程序编程接口密钥)是服务端为每个合法调用方分配的唯一标识字符串,用于识别调用方身份并控制资源访问。它是 RESTful API 中最简单、最轻量的认证方式,适合服务端到服务端、微服务间或开放平台场景。

3.1 API Key 的生成与分发

一个安全的 API Key 需要满足 不可预测性、唯一性 和 可撤销性。生成过程通常包含以下步骤:

  1. 熵源采集:使用系统级随机数生成器(如 /dev/urandom、SecureRandom)采集足够熵值。
  2. 编码转换:将二进制随机字节编码为可打印字符串,常用 Base64 URL-safe 或 Hex 编码。
  3. 前缀标识:添加可读前缀(如 sk_live_、pk_test_)便于区分环境与类型。
  4. 哈希存储:服务端仅存储 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 可以通过三种位置传递给服务端,每种方式在安全性、缓存友好性和使用场景上各有差异。

API Key 三种传递方式对比 客户端 服务端 方式1: Header 传递 方式2: Query 参数传递 方式3: Cookie 传递 Header 最安全,不进入 URL 日志;Query 方便调试但易泄露;Cookie 适合浏览器端自动携带 OpenAPI 通过 securitySchemes.in 字段指定传递方式

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 的作用域机制,需在业务逻辑中自行实现。