路由断言工厂 (Route Predicate Factories)
Spring Cloud Gateway 将路由匹配作为 Spring WebFlux HandlerMapping 基础设施的一部分。 Spring Cloud Gateway 包含许多内置的路由断言工厂。所有这些断言都匹配 HTTP 请求的不同属性。 你可以使用逻辑 and 语句组合多个路由断言工厂。
1. After 路由断言工厂
After 路由断言工厂接受一个参数:datetime(即 Java 的 ZonedDateTime)。 该断言匹配发生在指定日期时间之后的请求。
application.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]此路由匹配 2017 年 1 月 20 日 17:42(丹佛时间)之后发出的任何请求。
2. Before 路由断言工厂
Before 路由断言工厂接受一个参数:datetime(即 Java 的 ZonedDateTime)。 该断言匹配发生在指定日期时间之前的请求。
application.yml
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]此路由匹配 2017 年 1 月 20 日 17:42(丹佛时间)之前发出的任何请求。
3. Between 路由断言工厂
Between 路由断言工厂接受两个参数:datetime1 和 datetime2(即 Java 的 ZonedDateTime 对象)。 该断言匹配发生在 datetime1 之后且 datetime2 之前的请求。datetime2 参数必须在 datetime1 之后。
application.yml
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]此路由匹配 2017 年 1 月 20 日 17:42 到 2017 年 1 月 21 日 17:42(丹佛时间)之间发出的任何请求。 这对维护窗口期非常有用。
4. Cookie 路由断言工厂
Cookie 路由断言工厂接受两个参数:Cookie name(名称)和 regexp(Java 正则表达式)。 该断言匹配具有给定名称且值与正则表达式匹配的 Cookie。
application.yml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p此路由匹配请求中包含名为 chocolate 且值匹配 ch.p 正则表达式的 Cookie。
5. Header 路由断言工厂
Header 路由断言工厂接受两个参数:header(名称)和 regexp(Java 正则表达式)。 该断言匹配具有给定名称且值与正则表达式匹配的 Header。
application.yml
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+如果请求包含名为 X-Request-Id 的 Header,且其值匹配 \d+ 正则表达式(即值为一个或多个数字),则匹配该路由。
6. Host 路由断言工厂
Host 路由断言工厂接受一个参数:主机名 patterns(模式)列表。 该模式是 Ant 风格的模式,以 . 作为分隔符。 该断言匹配与模式匹配的 Host Header。
application.yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org同样支持 URI 模板变量(例如 {sub}.myhost.org)。
此路由匹配 Host Header 值为 www.somehost.org、beta.somehost.org 或 www.anotherhost.org 的请求。
该断言会将 URI 模板变量(如上例中定义的 sub)提取为名称和值的映射,并将其放入 ServerWebExchange.getAttributes() 中,键为 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE。 这些值随后可供 GatewayFilter 工厂 使用。
7. Method 路由断言工厂
Method 路由断言工厂接受一个 methods 参数,它可以是一个或多个参数:即要匹配的 HTTP 方法。
application.yml
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST如果请求方法是 GET 或 POST,则匹配该路由。
8. Path 路由断言工厂
Path 路由断言工厂接受两个参数:Spring PathMatcher patterns(模式)列表和一个可选的标志 matchTrailingSlash(默认为 true)。
application.yml
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}此路由匹配路径为 /red/1、/red/1/、/red/blue 或 /blue/green 的请求。
如果 matchTrailingSlash 设置为 false,则请求路径 /red/1/ 将不会被匹配。
如果你设置了 spring.webflux.base-path 属性,它将影响路径匹配。该属性值将自动添加到路径模式之前。例如,如果 spring.webflux.base-path=/app 且路径模式为 /red/{segment},则用于匹配的完整模式将是 /app/red/{segment}。
该断言会将 URI 模板变量(如上例中定义的 segment)提取为名称和值的映射,并将其放入 ServerWebExchange.getAttributes() 中,键为 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE。 这些值随后可供 GatewayFilter 工厂 使用。
可以使用一个工具方法(名为 get)来更轻松地访问这些变量:
Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");9. Query 路由断言工厂
Query 路由断言工厂接受两个参数:一个必需的 param 和一个可选的 regexp(Java 正则表达式)。
application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green如果请求包含 green 查询参数,则匹配上述路由。
application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.如果请求包含名为 red 的查询参数,且其值匹配 gree. 正则表达式(例如 green 和 greet),则匹配上述路由。
10. RemoteAddr 路由断言工厂
RemoteAddr 路由断言工厂接受一个 sources 列表(最小大小为 1),即 CIDR 符号(IPv4 或 IPv6)字符串,例如 192.168.0.1/16(其中 192.168.0.1 是 IP 地址,16 是子网掩码)。
application.yml
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24如果请求的远程地址是 192.168.1.10,则匹配该路由。
修改远程地址解析方式
默认情况下,RemoteAddr 路由断言工厂使用传入请求的远程地址。如果 Spring Cloud Gateway 位于代理层后面,这可能与实际的客户端 IP 地址不匹配。
你可以通过设置自定义的 RemoteAddressResolver 来定义远程地址的解析方式。 Spring Cloud Gateway 提供了一个非默认的远程地址解析器 XForwardedRemoteAddressResolver,它基于 X-Forwarded-For header。
XForwardedRemoteAddressResolver 有两个静态构造方法,采用了不同的安全策略:
XForwardedRemoteAddressResolver::trustAll:返回一个RemoteAddressResolver,它总是获取X-Forwarded-For头中发现的第一个 IP 地址。这种方法容易受到欺骗,因为恶意客户端可以为X-Forwarded-For设置一个初始值,解析器会接受该值。XForwardedRemoteAddressResolver::maxTrustedIndex:接受一个索引,该索引与运行在 Spring Cloud Gateway 前面的受信任基础设施的数量相关。例如,如果 Spring Cloud Gateway 只能通过 HAProxy 访问,则应使用值 1。如果在访问 Spring Cloud Gateway 之前需要两跳受信任的基础设施,则应使用值 2。
考虑以下 Header 值:
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3以下 maxTrustedIndex 值将产生以下远程地址:
maxTrustedIndex | 结果 |
|---|---|
[Integer.MIN_VALUE, 0] | (无效, 初始化时抛出 IllegalArgumentException) |
| 1 | 0.0.0.3 |
| 2 | 0.0.0.2 |
| 3 | 0.0.0.1 |
[4, Integer.MAX_VALUE] | 0.0.0.1 |
以下示例展示了如何使用 Java 实现相同的配置:
GatewayConfig.java
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(1);
...
.route("direct-route",
r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
.uri("https://downstream1")
.route("proxied-route",
r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
.uri("https://downstream2")
)11. Weight 路由断言工厂
Weight 路由断言工厂接受两个参数:group 和 weight(一个整数)。权重是按组计算的。
application.yml
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2此路由会将约 80% 的流量转发到 weighthigh.org,约 20% 的流量转发到 weightlow.org。
12. XForwarded Remote Addr 路由断言工厂
XForwarded Remote Addr 路由断言工厂接受一个 sources 列表(最小大小为 1),即 CIDR 符号(IPv4 或 IPv6)字符串,例如 192.168.0.1/16。
此路由断言允许基于 X-Forwarded-For HTTP Header 过滤请求。
这可以与反向代理(如负载均衡器或 Web 应用程序防火墙)一起使用,在这些场景中,只有当请求来自这些反向代理使用的受信任 IP 地址列表时,才允许请求通过。
application.yml
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24如果 X-Forwarded-For Header 包含(例如)192.168.1.10,则匹配该路由。
个人笔记
(先记住一句话:所谓 Predicate 就是“请求满足什么条件才走这条路由”——相当于安检门的“准入规则”。)
1. 时间三兄弟:After / Before / Between
| 名字 | 作用 | 一句话记忆 | 例子 |
|---|---|---|---|
| After | 只有在“这个时间点之后”来的请求才放行 | “过了点才开门” | - After=2025-06-01T00:00:00+08:00[Asia/Shanghai] |
| Before | 只有在“这个时间点之前”来的请求才放行 | “过期不候” | 同上,把 After 换成 Before |
| Between | 只有在“这两时间点之间”来的请求才放行 | “限时开放” | - Between=2025-06-01T00:00:00+08:00[Asia/Shanghai], 2025-06-03T23:59:59+08:00[Asia/Shanghai] |
时间格式固定:
yyyy-MM-dd'T'HH:mm:ss.SSSXXX[时区],直接抄示例改数字最稳。
2. Cookie 检查官
作用:请求必须带指定 Cookie 名 + 值匹配正则 才放行。
例子:
- Cookie=session, \d+ # 必须带名为 session 的 Cookie,且值是纯数字生活场景:门禁卡必须写着你名字并且号码是“数字”才让你进。
3. Header 检查官
作用:请求头必须存在某 key + 值匹配正则。
例子:
- Header=X-Request-Id, \d+ # 请求头里要有 X-Request-Id 且值是数字生活场景:安检要看你工牌编号是不是“数字”。
4. Host 域名匹配
作用:根据 Host 头 做“模糊匹配”。
例子:
- Host=**.api.example.com # 任何三级域名都可:foo.api.example.com、bar.api.example.com生活场景:公司园区有多个门,只要门牌后缀是 .api.example.com 都能进。
5. Method 方法匹配
作用:只允许指定 HTTP 方法。
例子:
- Method=GET,POST # 只接 GET 或 POST,PUT/DELETE 直接 4046. Path 路径匹配(最常用)
作用:根据 URL 路径 做匹配,还能顺手提取变量。
例子:
- Path=/order/{orderId}/detail # 匹配 /order/123/detail,同时把 123 存进变量 orderId高级开关:matchTrailingSlash=false 让 /order/123/ 不匹配(默认是 true,带不带斜杠都算)。
7. Query 参数匹配
作用:要求必须带某个 查询参数,可附加正则。
例子:
- Query=green # 只要有 ?green 就行,值无所谓
- Query=red, gree. # 必须带 ?red=xxx 且值满足正则 gree.(如 green、greet)8. RemoteAddr / XForwardedRemoteAddr
作用:只看 客户端 IP 是否落在指定网段。
区别:
- RemoteAddr:直接拿 TCP 连接 IP,网关前面没代理才准。
- XForwardedRemoteAddr:拿
X-Forwarded-For头,前面有 Nginx / SLB 时必须用它。
例子:
- RemoteAddr=192.168.1.0/24 # 只允许 192.168.1.* 网段配合代理:
// Java 配置示例:信任前面只有 1 层代理
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(1);9. Weight 权重分流
作用:把流量按权重分到不同后端,做灰度 / A/B。
例子:
# 80% 流量去 weighthigh.org
- id: weight_high
predicates:
- Weight=group1, 8
# 20% 流量去 weightlow.org
- id: weight_low
predicates:
- Weight=group1, 2生活场景:10 张抽奖券,8 张写 A 奖,2 张写 B 奖,随机抽。
10. 组合使用(AND 关系)
一句话:同一路由里多个 predicate 默认是“且”关系。
例子:
predicates:
- Path=/api/**
- Method=POST
- Header=X-Version, v2含义:只有 POST 方式 访问 /api/** 且 带 X-Version:v2 头,才走这条路由。
速查表(保存即可)
| 场景 | 直接抄 |
|---|---|
| 限定时间窗口 | - Between=2025-06-01T00:00:00+08:00[Asia/Shanghai], 2025-06-03T23:59:59+08:00[Asia/Shanghai] |
| 必须带某 Cookie | - Cookie=session, \d+ |
| 只接 GET | - Method=GET |
| 提取路径变量 | - Path=/user/{userId} |
| 只允许内网 IP | - RemoteAddr=10.0.0.0/8 |
| 80%/20% 灰度 | 两条路由,分别 - Weight=group1, 8 和 - Weight=group1, 2 |
最后口诀
“时间用 After,灰度用 Weight,路径变量用 Path,IP 限制看 Remote,其他 Header Cookie Query 都能拦。”
拿着这张速查表,10 秒钟就能写出一条新路由。