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。

下面这张图把四种方案在网关上的凭证形态、校验动作与后端可见信息放在一起对比,便于快速定位差异。

四种认证方案在网关上的落地对比 客户端 X-API-Key Basic 头 Bearer JWT Bearer AT API 网关认证层 API Key 校验 查缓存/库 → 命中则放行 可绑定配额与限速 Basic Auth 校验 Base64 解码 → 比对凭据 必须走 TLS,否则明文暴露 JWT 校验 验签 + exp/nbf + iss/aud 本地完成,无需回源 OAuth2.0 校验 验签 + scope 检查 必要时 introspect 回源 后端服务 X-Consumer-Id X-User-Name X-Sub / X-Scope X-Sub / X-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 并给出明确原因:

  1. 验签:用授权方公钥(RS256/ES256)或共享密钥(HS256)验证签名。HS256 的密钥绝不能下发到客户端,否则任何人都能伪造令牌。
  2. 校验时间声明:检查 exp(过期)与 nbf(生效时间),并允许少量时钟偏移,例如 30 秒。
  3. 校验签发方与受众:iss 必须是可信的授权服务器,aud 必须包含本网关或本 API 的标识,防止 A 系统的令牌被拿到 B 系统使用。
  4. 提取声明并注入下游:把 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、失败原因,但不记录凭证本身),这样认证层才真正成为可运营、可审计的基础设施。