3. 身份认证基础
API 网关是南北向流量的统一入口,它最先接触请求,也最适合承担身份认证的第一道判断。把认证放在网关而不是每个后端服务里,好处是策略集中、密钥不扩散、审计口径一致;代价是网关必须理解不同认证方案的语义差异,并在转发前把"调用者是谁"这一结论可靠地传给后端。本节围绕四种最常见的方案——API Key、Basic Auth、JWT、OAuth2.0——讲清它们在网关上的落地方式与取舍。
四种方案的本质差异
先建立一个判断框架:认证方案的核心区别在于"凭证是否自包含"和"是否引入第三方授权服务器"。
- API Key:一串不透明字符串,本身不携带任何身份信息,网关必须查库或查缓存才能知道它属于谁。简单、易轮换,但无法表达有效期和权限范围,泄露即等同身份泄露。
- Basic Auth:把
用户名:密码做 Base64 编码后放在Authorization头。注意 Base64 不是加密,只是编码,明文等价,因此必须配合 TLS。它适合内部工具、调试接口,不适合面向公网的开放 API。 - JWT:自包含令牌,头部、载荷、签名三段用点号连接。网关只需用公钥或共享密钥验签,即可在本地判断令牌真伪与是否过期,无需每次回源查询,这是它相比 API Key 最大的性能优势。
- OAuth2.0:不是一种令牌格式,而是一套授权框架。它定义了客户端、资源所有者、授权服务器、资源服务器四种角色和多种授权模式。网关通常扮演资源服务器前的守卫,校验 Access Token(常为 JWT 格式)并检查 scope。
下面这张图把四种方案在网关上的凭证形态、校验动作与后端可见信息放在一起对比,便于快速定位差异。
API Key 的落地要点
API Key 的校验逻辑简单,但工程上有两个容易踩的坑。第一是存储:绝不能明文存库,应存哈希值(如 SHA-256),比对时对来值做同样哈希再查。第二是查找性能:Key 通常作为主键或唯一索引,配合 Redis 缓存,把"Key → 消费者身份 + 配额策略"的映射放在内存里,避免每个请求都打数据库。
一个典型的网关配置片段(以声明式配置为例)大致如下:
routes:
- name: order-api
paths: ["/api/orders/**"]
plugins:
key-auth:
header_name: X-API-Key
key_in_query: false
hide_credentials: true # 转发前剥离凭证,避免泄露到后端日志
rate-limiting:
key: consumer_id
limit: 1000
window: 60
这里 hide_credentials: true 很关键:认证完成后应把原始凭证从请求中移除,防止它被后端日志、链路追踪系统意外记录。认证结果通过 X-Consumer-Id 之类的内部头传给后端,后端只信任来自网关的这组头。
Basic Auth 的边界
Basic Auth 在网关上就是解码 Authorization: Basic base64(user:pass),然后比对用户凭据。它的价值在于兼容性极好,几乎所有 HTTP 客户端原生支持。但要注意三点:一是必须强制 HTTPS,否则等同于明文传密码;二是浏览器会缓存凭据并在同域自动重发,登出体验差;三是无法携带权限范围,只能表达"是谁",不能表达"能做什么"。因此它更适合内部管理接口、CI 拉取制品等场景,而不是开放 API。
JWT 校验:网关最该做对的四件事
JWT 是网关认证的主力。一个 JWT 由 header.payload.signature 三段组成,网关校验时按顺序做四件事,任何一步失败都应返回 401 并给出明确原因:
- 验签:用授权方公钥(RS256/ES256)或共享密钥(HS256)验证签名。HS256 的密钥绝不能下发到客户端,否则任何人都能伪造令牌。
- 校验时间声明:检查
exp(过期)与nbf(生效时间),并允许少量时钟偏移,例如 30 秒。 - 校验签发方与受众:
iss必须是可信的授权服务器,aud必须包含本网关或本 API 的标识,防止 A 系统的令牌被拿到 B 系统使用。 - 提取声明并注入下游:把
sub、scope、tenant_id等写入内部头,供后端做细粒度授权。
aud 和 iss。这会让令牌在多个系统间"串用",是横向越权的典型来源。另一个错误是把 alg 交给令牌自己决定,攻击者可改成 none 绕过验签,网关必须固定允许的算法白名单。OAuth2.0 在网关的角色
OAuth2.0 解决的是"第三方应用在用户授权下访问资源"的问题。网关在其中扮演资源服务器的前置守卫,核心动作是校验 Access Token 并检查 scope 是否覆盖当前接口所需权限。落地时有两条路径:
- 本地验签:Access Token 是 JWT 时,网关用授权服务器公钥本地验签,性能最好,适合高并发。
- 令牌自省:Token 是不透明字符串时,网关调用授权服务器的
/introspect接口确认有效性。准确但每次请求多一次网络往返,必须加缓存,缓存时间不超过令牌剩余有效期。
scope 检查要落到具体接口上。例如订单查询接口要求 orders:read,网关在验签后比对令牌 scope 声明是否包含该值,不包含则返回 403 而非 401——401 表示"未认证",403 表示"已认证但无权限",这个区分对客户端排障很重要。
选型与组合建议
| 方案 | 凭证自包含 | 是否需回源 | 可表达权限 | 典型场景 |
|---|---|---|---|---|
| API Key | 否 | 查缓存/库 | 弱(靠绑定策略) | 开放平台、服务间简单调用 |
| Basic Auth | 否 | 比对凭据 | 无 | 内部工具、CI、调试接口 |
| JWT | 是 | 否 | 强(scope/claims) | 微服务间、前后端分离 |
| OAuth2.0 | 视令牌而定 | 可选 | 强(scope) | 第三方授权、开放 API |
实践中常见组合是:对外用 OAuth2.0 发放 JWT 格式的 Access Token,网关本地验签并检查 scope;对内服务间调用用 API Key 做粗粒度识别,再叠加 mTLS 保证链路可信。无论选哪种,网关都应统一输出认证结果头、统一 401/403 语义、统一记录认证失败日志(含请求 ID、来源 IP、失败原因,但不记录凭证本身),这样认证层才真正成为可运营、可审计的基础设施。