☰
SpringCloud集成微服务API网关Gateway(详解):从路由断言到统一鉴权的可复制配置
2026/10/17 17:17:44 网站建设 项目流程

1. 为什么微服务入口需要一个 Gateway:从订单服务被刷爆说起

如果你正在用 SpringCloud 做微服务,大概率会遇到这样一个场景:前端同学拿着三个不同的域名来找你,说订单、支付、用户服务各调各的,跨域配了三遍还是报错;运维同学又跑来说,某个服务没做鉴权,被人直接扫到了内部接口。这时候你需要的不是给每个服务再写一遍过滤器,而是在所有微服务前面放一个统一入口,这就是 SpringCloud Gateway 要解决的问题。

SpringCloud Gateway 是 SpringCloud 官方推出的 API 网关组件,基于 Spring5 的 WebFlux 响应式编程模型实现,和早期基于 Servlet 阻塞式模型的 Zuul 相比,在高并发场景下资源占用更低、吞吐更好。它能做的事情可以概括成五类:路由转发,把/orderservice/**的请求转给订单服务;负载均衡,同一个服务起三个实例时自动分发;统一鉴权,在网关层校验 Token,不合法的请求根本进不了微服务;流量控制,按请求频率限流;监控日志,所有进出流量在网关层统一记录。

这篇文章面向的是已经搭好 SpringCloud 工程、注册中心用 Nacos、服务间调用用 OpenFeign 的开发者。我会从零搭一个cloud-gateway模块,把路由断言、过滤器链、全局鉴权、跨域配置一次性配好,并且给出用 curl 验证每一步的具体命令。网关层跑通之后,我还会顺带讲一下怎么把网关后面的模型调用通道统一收口到 TaoToken 的 API 通道上,让 Key 管理也集中到网关这一层,避免每个服务各存一份密钥。

适合谁看:能看懂application.yml、写过@RestController、知道 Nacos 是干什么的 Java 后端。如果你连 SpringBoot 工程都没建过,建议先把基础工程跑起来再回来。整篇的节奏是先讲清楚问题,再给可复制配置,然后验证,最后排错,你可以边看边在本地敲。

2. TaoToken 前置准备:把模型调用通道收口到网关层

在讲网关配置之前,先说一下为什么要在网关这个位置接入 TaoToken。微服务架构里,订单服务、支付服务、客服服务可能都需要调用大模型能力,如果每个服务各自维护一份 API Key,密钥散落在各个配置文件里,轮换一次要改五六个地方,还容易漏。更合理的做法是让所有模型请求先经过网关,由网关统一持有 Key 并转发,微服务只面向网关的内网地址。

TaoToken 提供的是统一的模型 API 通道,官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。它的作用是把不同模型的调用协议统一成一套兼容 OpenAI 格式的接口,你在网关里只需要配一个上游地址和一个 Key,后面接哪个模型由请求里的 model 字段决定。

前置准备分三步。第一步,注册账号后进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面创建一个 Key,页面地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,创建后立刻复制保存,页面刷新后就不再完整显示。第二步,确认你要用的模型 ID,可以在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 里先手动发一条消息,确认通道可用,同时记下模型名称。第三步,如果你打算长期做编码类 Agent 或者多轮工具调用,可以看一下 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它面向的是持续编码场景,和按次调用的计费方式不同。

这里要强调一点:TaoToken 是合规的 API 通道服务,你在网关里配置它,本质上是把网关当成一个反向代理,把/ai/**的请求转发到https://taotoken.net/api并附上 Authorization 头。网关本身不改变请求语义,也不做任何协议转换之外的事情。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到字段对不上时以文档为准。

准备好 Key 和模型 ID 之后,我们进入网关模块的搭建。注意,网关层持有 Key 意味着这个模块的配置文件敏感度最高,生产环境建议用配置中心加密或者环境变量注入,不要明文提交到 Git。

3. 可复制配置:application.yml 路由断言与过滤器链

先建模块。在父工程下新建cloud-gateway模块,pom.xml里引入两个核心依赖:网关依赖和服务发现依赖。如果你用bootstrap.yml,还要额外引入 bootstrap 启动器,否则配置不会被加载;图省事的话直接用application.yml也可以。

<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> </dependencies>

这里有个高频坑:如果你的网关模块里混进了spring-boot-starter-web,启动会直接报Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway。解决办法是移除 web 依赖,或者在配置里显式声明spring.main.web-application-type: reactive。我建议直接移除,避免后面有人手滑又加回来。

下面是完整的application.yml,包含 Nacos 注册、两条路由、默认过滤器、全局跨域,以及一条指向 TaoToken 的 AI 通道路由。路径和原文保持一致,你可以直接复制后改服务名。

server: port: 10717 spring: application: name: gateway profiles: active: dev main: web-application-type: reactive cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: HZ namespace: cdd2a801-3280-4476-8362-d46318aac1a3 gateway: routes: - id: orderservice uri: lb://orderservice predicates: - Path=/orderservice/** filters: - AddRequestHeader=Authorization,token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA - id: paymentservice uri: lb://paymentservice predicates: - Path=/paymentservice/** - id: taotoken-ai uri: https://taotoken.net/api predicates: - Path=/ai/** filters: - StripPrefix=1 - AddRequestHeader=Authorization,Bearer ${TAOTOKEN_API_KEY} default-filters: - AddRequestHeader=X-Gateway-Source,cloud-gateway globalcors: add-to-simple-url-handler-mapping: true cors-configurations: '[/**]': allowedOrigins: - "http://localhost:10717" - "http://localhost:8088" allowedMethods: - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" allowCredentials: true maxAge: 360000

逐段解释。routes下每条路由有四个要素:id是唯一标识,随便起但别重复;uri支持http和lb两种,lb://orderservice表示走负载均衡到 Nacos 里注册的 orderservice 实例;predicates是断言,Path=/orderservice/**表示路径以/orderservice/开头就命中;filters是这条路由专属的过滤器。

default-filters里的过滤器对所有路由生效,我加了一个X-Gateway-Source头,方便后端服务确认请求确实经过了网关。globalcors解决跨域,add-to-simple-url-handler-mapping: true是为了让 OPTIONS 预检请求不被拦截,这个不加的话前端会一直报跨域。

taotoken-ai这条路由值得单独说。StripPrefix=1会把/ai前缀去掉,所以请求/ai/v1/chat/completions转发到上游时变成https://taotoken.net/api/v1/chat/completions。AddRequestHeader把 Key 注入到 Authorization 头,${TAOTOKEN_API_KEY}从环境变量读取,避免明文。启动前记得export TAOTOKEN_API_KEY=你的Key。

启动类很简单:

@SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); System.out.println("Gateway 网关服务启动成功"); } }

启动后看控制台有没有报错,然后打开 Nacos 控制台确认gateway服务已经注册进去。如果注册不上,先检查server-addr和namespace是否和你的 Nacos 一致。

4. 验证请求:用 curl 跑通路由转发与鉴权拦截

配置写完不验证等于没写。这一节我用 curl 把路由转发、默认过滤器、全局鉴权、AI 通道四条链路都跑一遍,每条都给出预期结果。

先验证基础路由转发。假设 orderservice 里有个接口/system/order/getToken,它返回请求头里的 Authorization:

curl -i "http://localhost:10717/orderservice/system/order/getToken"

预期返回 200,body 里data字段是token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA,说明路由转发成功,且路由过滤器注入的请求头被后端读到了。如果返回 404,检查 orderservice 是否注册到同一个 namespace,以及 Path 断言是否写对。

再验证默认过滤器。paymentservice 没有单独配 Authorization 过滤器,但 default-filters 会生效:

curl -i "http://localhost:10717/paymentservice/payment/getToken"

预期同样能拿到token-Bearer-...,证明默认过滤器对所有路由生效。这一步能过,说明过滤器链的合并逻辑正常。

接着验证全局鉴权。写一个AuthorizeFilter实现GlobalFilter和Ordered,从 query 参数里取 Authorization,和静态 Token 比对,不合法直接返回 401:

@Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final String STATIC_TOKEN = "token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String, String> params = request.getQueryParams(); String authToken = params.getFirst("Authorization"); if (STATIC_TOKEN.equals(authToken)) { return chain.filter(exchange); } exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } @Override public int getOrder() { return -1; } }

错误请求:

curl -i "http://localhost:10717/paymentservice/payment/getToken?Authorization=1234"

预期返回HTTP/1.1 401 Unauthorized。正确请求:

curl -i "http://localhost:10717/paymentservice/payment/getToken?Authorization=token-Bearer-5S3ZURuoTkN95zkG1QQ7eo7AHS7WA"

预期返回 200 和正常 JSON。这两条一对比,鉴权拦截就验证完了。

最后验证 AI 通道。先确认环境变量已导出,然后:

curl -i -X POST "http://localhost:10717/ai/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{"model":"你的模型ID","messages":[{"role":"user","content":"你好"}]}'

预期返回 200 和模型回复。如果返回 401,说明 Key 没注入成功,检查环境变量名是否和 yml 里一致。这一步跑通,意味着网关不仅能转发内部微服务,还能作为模型调用的统一出口。

5. 本篇常见错排查:401、local proxy failed 与 reading choices

网关配置踩坑集中在几个固定报错上,我把它们和对应原因列出来,你对照着改。

第一个是启动报Spring MVC found on classpath。原因前面说过,网关模块混进了spring-boot-starter-web。解决方式是移除该依赖,或者加spring.main.web-application-type: reactive。我建议移除,因为响应式网关和 Servlet 容器同时存在时,过滤器链行为会变得不可预测。

第二个是请求返回 401,但你的 Token 明明是对的。常见原因有三个:一是全局过滤器的 order 值太大,排在路由过滤器后面执行,导致请求已经被转发;二是 Token 从 header 取但你写成了 query 参数;三是AddRequestHeader注入的 Key 里多了空格。排查时先在过滤器里打印exchange.getRequest().getHeaders(),确认请求头内容。

第三个是local proxy failed或者连接被拒。这通常发生在uri写成了http://127.0.0.1:18088但服务实际端口不是这个,或者服务没注册到 Nacos 导致lb://解析不到实例。用curl直接打服务端口确认服务活着,再去 Nacos 控制台看实例列表。

第四个是调用 AI 通道时返回reading choices相关错误,或者响应体解析失败。这类报错一般不是网关的问题,而是上游返回了非预期结构。先用curl直接打https://taotoken.net/api/v1/chat/completions确认通道本身可用,再对比网关转发后的请求体是否被StripPrefix改错了路径。如果路径多剥了一层或少剥了一层,choices字段自然读不到。

第五个是跨域仍然报错。检查add-to-simple-url-handler-mapping是否为 true,以及allowedOrigins里有没有包含你前端的实际地址。注意allowCredentials: true时allowedOrigins不能用*,必须写具体域名。

第六个是过滤器执行顺序不符合预期。记住规则:order 值越小越先执行;路由过滤器和默认过滤器的 order 由 Spring 按声明顺序从 1 递增;order 相同时按默认过滤器、路由过滤器、全局过滤器的顺序。想看真实顺序,在FilteringWebHandler的handle方法打断点,或者把日志级别调到 DEBUG。

如果你在网关后面接的是 Claude Code 这类编码工具,配置时三件套要写全:Base URL 填https://taotoken.net/api,Key 填你创建的 Key,Model ID 填控制台里确认的模型名。缺任何一个都会报鉴权或模型不存在。接入细节以 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 为准。

6. 把网关作为统一出口:Key 管理与后续扩展

网关跑通之后,你会发现它的价值不只是路由。所有外部请求都从这里进,意味着鉴权、限流、日志、Key 管理都可以收口在这一层。我现在的做法是:微服务之间用内网直连,只有网关暴露公网端口;模型调用的 Key 只存在网关的环境变量里,业务服务通过网关的/ai/**路由间接调用,服务本身不接触密钥。

这样做的好处是轮换 Key 时只改一个地方,重启网关即可,业务服务无感。如果你后面要接多个模型供应商,也可以在网关层加一条路由,用不同的 Path 前缀区分,比如/ai-a/**和/ai-b/**,各自注入不同的 Key。再进一步,可以结合RequestRateLimiter过滤器做限流,防止某个服务把配额打满。

需要提醒的是,网关层持有 Key 也意味着它是安全等级最高的模块。生产环境建议把 Key 放到配置中心并加密,或者用 K8s Secret 注入环境变量,不要写死在 yml 里提交到代码仓库。另外,全局过滤器里做鉴权时,记得把健康检查路径和登录接口放行,否则 Nacos 的健康检查会被 401 拦掉。

如果你打算长期用这套架构做编码 Agent 或者多轮工具调用,可以了解一下 Coding Plan,它面向的是持续编码场景,和按次调用的计费方式不同,适合把网关作为统一出口后长期跑任务的团队。模型对话页面可以用来快速验证某个模型 ID 是否可用,省得每次都发 curl。

最后留一个实操建议:把本文的application.yml存成模板,每接一个新服务只改id、uri和Path三处,其余照抄。过滤器链和跨域配置一次配好,后面基本不用动。网关这东西,配对了就一劳永逸,配错了就是所有请求一起挂,所以每次改完务必用 curl 把四条链路各跑一遍再上线。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询