3. OpenAPI 规范回顾:securitySchemes 与 security 的定义方式
在构建 API 鉴权拦截器之前,必须准确理解 OpenAPI 3.0/3.1 规范中关于安全描述的两个核心组件:securitySchemes(安全方案定义)与 security(安全要求声明)。它们是拦截器解析鉴权规则的元数据基础。
3.1 核心概念与职责划分
securitySchemes 与 security 在规范中承担不同职责,下表梳理了二者的定位差异:
| 组件 | 位置 | 职责 | 作用域 |
|---|---|---|---|
| securitySchemes | 根级 components.securitySchemes |
定义可用的鉴权方案类型、参数、流程 | 全局复用 |
| security | 根级 或 操作级(path+method) | 声明哪些安全方案适用于当前 API | 可覆盖、可组合 |
3.2 securitySchemes:安全方案定义
securitySchemes 位于 components.securitySchemes 下,每个方案通过唯一名称(如 BearerAuth、ApiKeyAuth)标识。OpenAPI 3.0/3.1 支持以下四种鉴权类型:
- http:基于 HTTP 标准鉴权头,如 Bearer、Basic、Digest。
- apiKey:通过 Header、Query 或 Cookie 传递的静态密钥。
- oauth2:OAuth 2.0 流程,需定义 flows(authorizationCode、clientCredentials 等)。
- openIdConnect:基于 OpenID Connect Discovery 的鉴权。
以下是一个典型的 securitySchemes 定义示例:
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
OAuth2Auth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/authorize
tokenUrl: https://auth.example.com/token
scopes:
read: 读取资源
write: 写入资源
bearerFormat 仅为描述性字段,拦截器不应依赖它做格式校验。JWT 的完整性验证应基于签名而非格式声明。
3.3 security:安全要求声明
security 字段声明 API 需要满足的安全条件。它可以在两个层级出现:
- 根级 security:作用于整个 API,作为默认安全要求。
- 操作级 security:作用于特定路径+方法,覆盖根级声明。
security 的值是一个数组,每个元素是一个对象,键为 securitySchemes 中定义的名称,值为该方案所需的 scope 列表(仅 OAuth2/OpenID Connect 需要)。
# 根级:全局要求 Bearer 令牌
security:
- BearerAuth: []
# 操作级:覆盖为 API Key + OAuth2 二选一
paths:
/admin/users:
get:
security:
- ApiKeyAuth: []
- OAuth2Auth: [admin:read]
3.4 安全方案与安全声明的映射关系
为了直观展示 securitySchemes 与 security 如何协作,下图描述了从规范定义到拦截器执行的映射流程:
3.5 OpenAPI 3.0 与 3.1 的关键差异
虽然 securitySchemes 和 security 的核心结构在 3.0 和 3.1 中保持一致,但以下差异值得注意:
| 维度 | OpenAPI 3.0 | OpenAPI 3.1 |
|---|---|---|
| 规范基础 | 独立 JSON Schema 草案 | 完全基于 JSON Schema 2020-12 |
| securitySchemes 类型 | 支持 http/apiKey/oauth2/openIdConnect | 同上,无新增类型 |
| bearerFormat 语义 | 描述性字段 | 描述性字段,无强制校验 |
| oauth2 flows 定义 | 固定字段(implicit/password/...) | 同上,但 implicit 已标记为废弃 |
| security 空数组含义 | 表示无安全要求 | 同上 |
3.6 常见定义模式与拦截器适配
在实际工程中,security 的定义通常遵循以下几种模式,拦截器需要针对每种模式做适配:
- 单一鉴权模式:全局只有一个 security 条目,如
security: - BearerAuth: []。拦截器只需检查 Bearer 令牌。 - 多选一模式:security 数组包含多个对象,如
security: - ApiKeyAuth: [] - BearerAuth: []。拦截器需依次尝试,任一通过即放行。 - 作用域限定模式:OAuth2 场景下指定 scope,如
security: - OAuth2Auth: [read, write]。拦截器需同时验证令牌和 scope 权限。 - 无鉴权端点:操作级 security 为空数组
security: [],覆盖根级要求。拦截器应跳过该端点的鉴权检查。
以下代码片段展示了一个拦截器如何根据 security 声明动态选择校验策略:
// 伪代码:根据 security 声明选择鉴权策略
for (SecurityRequirement req : operation.getSecurity()) {
for (String schemeName : req.keySet()) {
SecurityScheme scheme = components.getSecuritySchemes().get(schemeName);
switch (scheme.getType()) {
case "http":
if ("bearer".equals(scheme.getScheme())) {
return validateBearerToken(request);
}
break;
case "apiKey":
return validateApiKey(request, scheme.getIn(), scheme.getName());
case "oauth2":
return validateOAuth2(request, req.get(schemeName));
}
}
}
security: [])时,表示该端点显式声明无安全要求。拦截器必须正确处理此情况,不要错误地继承根级 security。
3.7 本章小结
securitySchemes 与 security 构成了 OpenAPI 安全描述的基石。securitySchemes 定义鉴权方案的"能力",security 声明 API 端点的"需求"。拦截器的核心工作就是建立从 security 声明到 securitySchemes 定义的解析链路,并据此执行相应的鉴权逻辑。理解这两者的结构、组合规则和版本差异,是后续实现拦截器测试验证与工程落地的必要前提。