当系统从一个单体应用拆分成多个微服务之后,最先撞上的问题往往不是服务之间怎么通信,而是外部请求该从哪里进来。如果让前端直接调用各个微服务的地址,不仅要处理一堆跨域问题,还等于把内部服务的细节全部暴露在外,安全性和可维护性都很差。网关的作用就是在请求与服务之间加一道统一入口,负责路由转发、鉴权、限流和日志记录。Spring Cloud Gateway 是 Spring 官方推出的网关实现,底层基于 Spring WebFlux 和 Project Reactor,性能比传统的 Zuul 1.x 好不少,与 Spring Boot 的整合也几乎是开箱即用。

一、搭建网关工程并引入依赖
先创建一个独立的模块,专门作为网关服务。有一点必须提前强调:Spring Cloud Gateway 是基于 WebFlux 构建的,它依赖 Reactor Netty 作为底层服务器,因此工程中不能再引入 spring-boot-starter-web。两者共存时启动会直接报错,提示无法将 DispatcherServlet 与 WebFlux 混用,这是新手最常踩的坑。
在父工程确定好 Spring Boot 与 Spring Cloud 的版本对应关系后,网关模块只需要引入以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<lt;dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>第二个依赖不是必须的,只有当你的服务注册到 Eureka、Nacos 等注册中心,希望通过服务名转发请求时才需要。如果暂时没有注册中心,也可以直接在路由里写死目标服务的 IP 和端口,先跑通流程。
版本对应关系建议查看 Spring Cloud 官方的 Release Train 说明,比如 Spring Boot 2.7.x 对应 Spring Cloud 2021.0.x。版本不匹配会出现各种奇怪的 Bean 注入失败问题,排查起来非常耗时,务必在项目初期就锁定好版本。
二、配置路由与断言规则
Spring Cloud Gateway 的核心概念有三个:Route(路由)是转发的完整规则,Predicate(断言)判断请求是否匹配该路由,Filter(过滤器)则在转发前后对请求和响应做加工。下面通过配置文件方式定义几条典型的路由规则:
server:
port: 9000
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: user-service-route
uri: http://localhost:8081
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
- id: order-service-route
uri: http://localhost:8082
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1上面配置的含义是:所有以 /api/user 开头的请求会被转发到 8081 端口的用户服务,StripPrefix=1 表示转发前去掉路径的第一级前缀,也就是把 /api/user/login 改写成 /user/login 再发给下游。如果不做这层剥离,下游服务就必须也带着 /api/user 前缀编写 Controller,路径会变得冗余。
断言的类型很丰富,除了 Path 之外,常用的还有 Method(限定 GET、POST 等)、Header(判断请求头)、Query(判断查询参数)、Before/After(限定时间窗口)。多个断言可以叠加,取交集生效。例如只允许携带 token 请求头的请求走某条路由:
predicates: - Path=/api/admin/** - Header=token, \d+
如果配置文件写起来太啰嗦,也可以用 Java 配置类的方式声明路由,适合规则需要动态计算的场景:
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service-route", r -> r
.path("/api/user/**")
.filters(f -> f.stripPrefix(1))
.uri("http://localhost:8081"))
.build();
}
}三、结合注册中心实现动态转发
写死 IP 和端口只能用于本地调试,生产环境必须接入注册中心。接入之后,路由的 uri 只需要写成 lb://服务名,网关会自动从注册中心拉取服务实例列表,并在客户端做负载均衡:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/api/user/**lb:// 前缀表示 LoadBalancer 协议,网关转发前会根据服务名解析出实际地址。开启 discovery.locator 之后,甚至不需要手动配置路由,直接用 http://网关地址/服务名/接口路径 就能访问,不过这种方式把服务名暴露在 URL 中,实际项目中一般关闭它,改用显式路由规则。
需要注意,从 Spring Cloud 2020 版本开始 Ribbon 已经被移除,负载均衡由 Spring Cloud LoadBalancer 接管,依赖中要引入 spring-cloud-starter-loadbalancer,否则启动时会报一个找不到 ReactiveLoadBalancerClientFilter 依赖的警告,请求也无法正常转发。
四、自定义过滤器处理鉴权与日志
网关最常见的业务需求是统一鉴权。实现方式是编写全局过滤器,实现 GlobalFilter 和 Ordered 接口,在请求转发前校验 Token:
@Component
public class AuthFilter implements GlobalFilter, Ordered {
private static final List<String> WHITE_LIST = List.of("/api/user/login", "/api/user/register");
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 白名单直接放行
if (WHITE_LIST.contains(path)) {
return chain.filter(exchange);
}
String token = exchange.getRequest().getHeaders().getFirst("token");
if (token == null || token.isEmpty()) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 校验通过后可以解析用户信息,塞进请求头传给下游
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", parseUserId(token))
.build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public int getOrder() {
return -100;
}
}getOrder 返回值越小,过滤器优先级越高,鉴权过滤器应该设置得比较靠前,避免未认证的请求先经过记录日志之类的过滤器。校验通过后通过 mutate 修改请求,把用户 ID 写入自定义请求头,下游服务直接从请求头取用户身份,不需要再解析一次 Token。
跨域问题也建议统一在网关处理,不要让下游服务各自配置,否则容易出现重复的 CORS 头导致浏览器报错:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedMethods: "*"
allowedHeaders: "*"
allowCredentials: true五、常见问题排查
整合过程中有几个高频报错值得记录。第一是启动报 Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway,原因就是前面说的引入了 web 依赖,检查依赖树用 mvn dependency:tree 找到传递引入的 starter-web 并排除即可。
第二是请求返回 503,通常是配置了 lb:// 但目标服务没有注册到注册中心,或者缺少 loadbalancer 依赖。可以在注册中心控制台确认服务是否在线。第三是返回 404,多半是 StripPrefix 的层级和下游接口路径对不上,可以临时开启日志观察实际转发的路径:
logging:
level:
org.springframework.cloud.gateway: debug
reactor.netty: debug把网关日志调成 debug 级别后,控制台会打印完整的路由匹配过程和最终转发地址,定位路径问题非常高效。掌握这些之后,一套具备路由转发、负载均衡和统一鉴权能力的网关服务就可以稳定支撑业务流量了,后续还可以在此基础上叠加 Sentinel 限流、灰度发布等高级能力。
Spring Boot GatewaySpring Cloud Gateway网关路由修改时间:2026-09-11 07:36:34