1. 多网关共存时,统一 Key 到底难在哪
如果你手上同时跑着 SpringGateway、Zuul、Kong、APISIX 这四类网关,大概率会遇到一个很现实的问题:每个网关都有自己的鉴权插件、自己的密钥存储、自己的限流配置。SpringGateway 里写 GlobalFilter 校验 Header,Zuul 里挂 ZuulFilter 做前置拦截,Kong 用 Key-Auth 插件,APISIX 用 consumer + key-auth。四套逻辑各写一遍,密钥轮换时要在四个地方同步改,漏一个就是线上事故。
我试过在一个项目里把上游模型调用分散到四个网关后面,结果最头疼的不是路由转发,而是「同一个 Key 在四个网关里表现不一致」——有的网关把 Key 放在Authorization,有的放在apikey,有的要求Bearer前缀。调用方每换一个入口就要改一次代码。
这篇要解决的问题就是:把 TaoToken 当作统一的 Key/API 通道,让四类网关都指向同一个上游地址和同一套密钥,网关本身只负责路由和转发,鉴权与密钥管理收敛到一处。TaoToken 在这里扮演的是「统一上游 + 统一 Key」的角色,它提供 OpenAI 兼容的 API 入口,网关只需要把请求转发过去、把 Key 透传或注入即可。
适合谁看:正在做微服务网关选型或迁移的后端同学;已经有多套网关、想统一鉴权链路的运维/架构同学;以及想本地快速跑通「网关 → TaoToken → 模型」这条链路的开发者。下面每个网关我都会给出可复制的配置骨架,并说明验证动作。
2. 前置准备:TaoToken 的 Key 与接入地址
在动网关配置之前,先把上游通道准备好。TaoToken 的 API 入口是https://taotoken.net/api,它兼容 OpenAI 的请求格式,所以四类网关的转发目标都可以统一写成这个地址。
第一步,去控制台创建一个 API Key。打开https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite,在 API Keys 页面新建一个 Key,复制出来形如sk-xxxx的字符串。这个 Key 就是后面四个网关共用的那一把,轮换时只改这一处。
第二步,确认你要转发的具体路径。TaoToken 的对话补全路径是/v1/chat/completions,模型列表是/v1/models。网关转发时,upstream指向https://taotoken.net/api,路径保留/v1/...即可。
第三步,本地先用 curl 验证 Key 可用,避免后面排查时分不清是网关问题还是 Key 问题:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的Key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}] }'返回里有choices字段就说明通道正常。这一步过了,再往下配网关。如果你还没决定用哪个模型,可以先去https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite看一眼可用列表,再回来配。
注意:网关里不要把 Key 硬编码进配置文件提交到 Git。下面示例用环境变量占位,实际部署时用配置中心或 Secret 注入。
3. 四类网关的可复制配置骨架
这一节是全文的核心。四类网关的配置思路一致:定义一个上游(TaoToken),定义一个路由(匹配/v1/**),把 Key 以 Header 形式注入或透传。区别在于各自的配置语法和插件机制。
3.1 SpringGateway:用 RouteLocator + 默认 Header
SpringGateway 基于 WebFlux,配置分 Java DSL 和 YAML 两种。推荐 YAML,改起来直观。核心是spring.cloud.gateway.routes里定义uri和predicates,再用default-filters注入 Authorization。
spring: cloud: gateway: default-filters: - AddRequestHeader=Authorization, Bearer ${TAOTOKEN_KEY} routes: - id: taotoken-chat uri: https://taotoken.net/api predicates: - Path=/v1/chat/completions - id: taotoken-models uri: https://taotoken.net/api predicates: - Path=/v1/models${TAOTOKEN_KEY}从环境变量读取,启动时传入。这样调用方访问网关的/v1/chat/completions,网关自动补上 Authorization 头转发到 TaoToken。如果调用方自己带了 Key,想透传而不是覆盖,把AddRequestHeader换成PreserveHostHeader之类的策略,或者干脆不加默认过滤器,让 Header 原样透传。
3.2 Zuul:ZuulFilter 做前置注入
Zuul 1.x 的配置在application.yml里定义路由,鉴权靠自定义ZuulFilter。路由部分:
zuul: routes: taotoken: path: /v1/** url: https://taotoken.net/api sensitive-headers:sensitive-headers留空是为了不让 Zuul 过滤掉 Authorization 头。然后写一个pre类型的过滤器:
@Component public class TaotokenAuthFilter extends ZuulFilter { @Value("${TAOTOKEN_KEY}") private String key; @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); ctx.addZuulRequestHeader("Authorization", "Bearer " + key); return null; } }Zuul 2.x 改成了异步模型,过滤器接口不同,但思路一样:在pre阶段往请求头塞 Authorization。
3.3 Kong:Service + Route + key-auth 插件
Kong 用 Admin API 或声明式配置(kong.yml)管理。声明式配置更适合版本化,骨架如下:
_format_version: "3.0" services: - name: taotoken-service url: https://taotoken.net/api routes: - name: taotoken-route paths: - /v1 strip_path: false plugins: - name: key-auth service: taotoken-service config: key_names: - apikey hide_credentials: false consumers: - username: taotoken-consumer keyauth_credentials: - key: sk-你的Key这里有个关键点:Kong 的key-auth插件默认从apikey头或 query 参数取 Key,而 TaoToken 期望的是Authorization: Bearer。两种做法——要么让调用方按 Kong 的apikey传,再用request-transformer插件改写成 Authorization;要么直接不用 key-auth,改用request-transformer注入固定 Header。后者更简单:
plugins: - name: request-transformer service: taotoken-service config: add: headers: - "Authorization:Bearer sk-你的Key"strip_path: false很重要,否则 Kong 会把/v1前缀吃掉,转发到 TaoToken 就变成根路径了。
3.4 APISIX:route + upstream + 插件链
APISIX 的配置也是声明式,config.yaml里定义 route 和 upstream,插件挂在 route 上。骨架:
routes: - uri: /v1/* name: taotoken-route upstream: type: roundrobin nodes: "taotoken.net:443": 1 scheme: https pass_host: node plugins: proxy-rewrite: regex_uri: - "^/v1/(.*)" - "/api/v1/$1" request-transformer: add: headers: - "Authorization: Bearer sk-你的Key"APISIX 的proxy-rewrite用来改写路径,因为上游是taotoken.net/api,而路由匹配的是/v1/*,需要把/v1/xxx重写成/api/v1/xxx。pass_host: node让 SNI 和 Host 用节点域名,避免 TLS 握手失败。
四类网关的配置对照可以看这张表:
| 网关 | 配置载体 | Key 注入方式 | 路径处理要点 |
|---|---|---|---|
| SpringGateway | YAML / Java DSL | default-filters AddRequestHeader | predicates 精确匹配 |
| Zuul | YAML + Java Filter | ZuulFilter pre 阶段 | sensitive-headers 留空 |
| Kong | kong.yml / Admin API | request-transformer 插件 | strip_path: false |
| APISIX | config.yaml | request-transformer 插件 | proxy-rewrite 改写前缀 |
4. 逐项验证:确认请求链路真的通了
配完不算完,要逐项验证。验证的核心是「请求经过网关后,TaoToken 能收到正确的 Key 和路径」。每个网关都用一个 curl 打自己的入口,看返回。
SpringGateway 和 Zuul 验证方式一样,打网关端口:
curl -s http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'Kong 默认代理端口 8000,APISIX 默认 9080:
curl -s http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"ping"}]}'如果返回choices,说明链路通了。如果返回 401,说明 Key 没注入成功;返回 404,说明路径被改写错了。这时候去看网关的访问日志,确认转发出去的 URL 和 Header 长什么样。
一个更细的验证动作:在 TaoToken 侧看请求日志(控制台有调用记录),确认收到的Authorization头是Bearer sk-xxx,路径是/api/v1/chat/completions。两边对上了,才算真正跑通。
5. 本篇常见错排查
401 Unauthorized,但 curl 直连 TaoToken 是好的。九成是网关把 Authorization 头吃掉了。Zuul 检查sensitive-headers是否留空;SpringGateway 检查有没有别的过滤器覆盖了 Header;Kong 检查hide_credentials是不是设成了 true。
404 Not Found,路径不对。Kong 的strip_path默认是 true,会把匹配的前缀删掉,必须设 false。APISIX 的proxy-rewrite正则写错也会导致路径拼接错误,用regex_uri时注意捕获组和替换串的对应关系。
TLS 握手失败或证书错误。APISIX 转发到 https 上游时,pass_host设成node让 SNI 用节点域名。Kong 的 service url 写https://时确认证书链没问题。
Key 轮换后部分网关失效。这就是多网关共存的典型坑——四个地方各存了一份 Key。解决办法是把 Key 收敛到环境变量或配置中心,四个网关都从同一处读。TaoToken 侧轮换 Key 后,只需更新配置中心的值,重启或热加载网关即可。
请求体被网关改写。有些网关默认会压缩或改写 body,导致模型接口报格式错误。检查网关有没有开request-transformer的 body 改写,或者proxy-rewrite动了 body。
6. 把 Key 收敛到一处,网关只管转发
四类网关配下来,你会发现真正需要维护的只有一把 Key 和一个上游地址。SpringGateway 用 default-filters,Zuul 用 pre 过滤器,Kong 和 APISIX 用 request-transformer 插件,本质都是「在转发前把 Authorization 补上」。网关的职责回归到路由和流量治理,鉴权和密钥管理交给 TaoToken 统一处理。
如果你后面要接更多模型或做长期编码任务,可以在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite看下 Coding Plan 的额度方案;需要管理多个 Key 或查看调用明细,去https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite操作;接入细节和参数说明在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite。本地想先验证模型通不通,直接用https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite里的对话入口试一句就行。
最后留一个实操建议:四个网关的配置文件里,Key 一律用${TAOTOKEN_KEY}占位,本地用.env,线上用 Secret。这样轮换时只改一处,四个网关同时生效,不会再出现「改了三个漏了一个」的情况。