bigywater.tech
微服务 · 33

网关鉴权:auth_request 交给独立服务,还是网关本地校验 JWT

网关鉴权用 auth_request 交给独立服务,还是网关本地校验 JWT?延迟差多少?令牌撤销多久生效?鉴权服务挂了怎么办?身份头会不会被伪造?这篇用 nginx 1.31.6 和 Envoy 1.39.3 逐项量出来。

29.34 ms 对 0.18 ms
auth_request(鉴权服务 20 ms)与 Envoy 本地 jwt_authn 的延迟中位数
0 / 2.6 s / 不会
令牌撤销的生效延迟:auth_request 无缓存 / 缓存 5 s / 本地 JWT(要等令牌过期;Envoy 默认再宽限 60 s)
admin
不覆盖 X-User 时,client 伪造的身份被后端当真;用 proxy_set_header X-User $auth_user 覆盖后是 alice

延迟

实测 · 本机执行输出
  做法                                | 延迟中位数    | p99      | 鉴权服务被调用 | 状态码       
  ----------------------------------+----------+----------+---------+-----------
  不鉴权                               | 0.15 ms  | 0.54 ms  | 0       | {200: 500}
  auth_request(鉴权服务 ~0 ms)          | 0.32 ms  | 0.51 ms  | 500     | {200: 500}
  auth_request(鉴权服务 20 ms)          | 29.34 ms | 33.15 ms | 500     | {200: 500}
  auth_request + 缓存 5 s(鉴权服务 20 ms) | 0.17 ms  | 0.57 ms  | 1       | {200: 500}
  Envoy 本地 jwt_authn                | 0.18 ms  | 0.34 ms  | 0       | {200: 500}

auth_request 每个请求多一次往返;加缓存可以把鉴权服务的调用降到几乎为零,本地校验没有往返。

撤销

实测 · 本机执行输出
  做法                                          | 撤销之后,最后一个被放行的请求距撤销多久 | 撤销之后第一个被拒绝的请求距撤销多久 | 撤销之后仍被放行的请求数
  --------------------------------------------+----------------------+--------------------+-------------
  auth_request 无缓存                            | 立刻                   | 0.00 s             | 0           
  auth_request 缓存 5 s                         | 2.34 s               | 2.60 s             | 10          
  Envoy 本地 jwt_authn(clock_skew_seconds 保持默认) | 11.36 s              | 没有拒绝               | 45          
  Envoy 本地 jwt_authn(clock_skew_seconds: 1)   | 10.60 s              | 10.86 s            | 42          
  本地校验 JWT 不查撤销名单,令牌在过期之前一直有效(令牌有效期 12 秒、撤销发生在第 3 秒,所以要等令牌过期才会被拒);Envoy jwt_authn 的 clock_skew_seconds 默认允许令牌过期之后再宽限一段时间(默认值下整个 14.5 秒的测试里令牌一直被放行,设成 1 才在过期时被拒(0 表示“用默认值”,不能用来关掉宽限));auth_request 每次问鉴权服务,撤销立刻生效;给鉴权结果加缓存,撤销的生效延迟最长就是缓存时间

auth_request 可以立刻撤销,缓存会延迟;本地校验无法撤销,只能等过期。

鉴权服务挂了

实测 · 本机执行输出
  做法                                                      | 第 0 秒 放行/失败 | 第 1 秒 放行/失败 | 第 2 秒 放行/失败 | 第 3 秒 放行/失败 | 第 4 秒 放行/失败 | 第 5 秒 放行/失败
  --------------------------------------------------------+-------------+-------------+-------------+-------------+-------------+------------
  auth_request 无缓存                                        | 50/0        | 50/0        | 0/50        | 0/50        | 0/50        | 0/50       
  auth_request 缓存 5 s,proxy_cache_use_stale error timeout | 50/0        | 50/0        | 50/0        | 50/0        | 50/0        | 50/0       
  auth_request 缓存 5 s,不允许用过期缓存                            | 50/0        | 50/0        | 50/0        | 50/0        | 50/0        | 16/34      
  Envoy 本地 jwt_authn(鉴权服务关不关都不影响)                         | 50/0        | 50/0        | 50/0        | 50/0        | 50/0        | 50/0       
  auth_request 是网关对鉴权服务的硬依赖:无缓存时鉴权服务一挂,所有需要鉴权的请求立刻失败;带缓存且不允许用过期缓存时,缓存有效期内的请求还能放行,缓存过期之后一起失败;加了 proxy_cache_use_stale error timeout 之后,鉴权服务挂了,过期的缓存继续被用,请求一直被放行——可用性好了,代价是撤销、封禁都不再生效(这是“失败时放行”的行为);本地校验的网关不依赖外部服务

auth_request 是硬依赖;缓存加 use_stale 会失败时放行;本地校验不受影响。

身份头

实测 · 本机执行输出
  配置                                           | 状态码 | 后端回应(hello + 它看到的 X-User)
  ---------------------------------------------+-----+--------------------------
  nginx (a) 不覆盖 X-User                         | 200 | hello admin              
  nginx (b) proxy_set_header X-User $auth_user | 200 | hello alice              
  Envoy jwt_authn(没有配置覆盖)                      | 200 | hello admin              
  网关鉴权通过之后,要把“已验证的身份”用网关自己的值覆盖到发给后端的头里,并且把 client 发来的同名头丢掉,否则后端会信任伪造的身份

网关要用自己确认的身份覆盖发给后端的头。

分步演示

网关鉴权
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。nginx 是 Homebrew 的 1.31.6(auth_request 与 proxy_cache);Envoy 1.39.3 的 jwt_authn 过滤器,密钥内联在配置里(HS256,kid k1);JWT 是我用 hmac 自己签发的;鉴权服务和后端是本进程里的 Python 服务;第三节“鉴权服务挂了”是停掉服务并断开已建立的连接(我最初只停了监听,nginx 到鉴权服务的长连接仍然能用,所以 Svc.stop 现在会同时断开已建立的连接)
    • 没有验证:RS256 / JWKS 远程拉取与轮转;jwt_authn 的 ext_authz 过滤器(Envoy 的 auth_request 等价物);OAuth2 introspection;令牌撤销用短有效期加刷新令牌的方案;nginx 商业版的 auth_jwt 模块;每种配置只测了一次
    京ICP备2026067502号-1京公网安备11010502063487号