路由谓词工厂
Spring Cloud Gateway 将路由匹配作为 Spring WebFlux HandlerMapping 基础设施的一部分。Spring Cloud Gateway 包含许多内置的路由断言工厂。所有这些断言都匹配 HTTP 请求的不同属性。您可以将多个路由断言工厂与逻辑 and 语句结合使用。
After 路由断言工厂
After 路由断言工厂接受一个参数:一个 datetime(即 Java ZonedDateTime)。此断言匹配在指定日期时间之后发生的请求。以下示例配置了一个 After 路由断言
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 山区时间(丹佛)之后发出的任何请求。
Before 路由断言工厂
Before 路由断言工厂接受一个参数:一个 datetime(即 Java ZonedDateTime)。此断言匹配在指定 datetime 之前发生的请求。以下示例配置了一个 Before 路由断言
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 山区时间(丹佛)之前发出的任何请求。
Between 路由断言工厂
Between 路由断言工厂接受两个参数:datetime1 和 datetime2,它们是 Java ZonedDateTime 对象。此断言匹配在 datetime1 之后且在 datetime2 之前发生的请求。datetime2 参数必须在 datetime1 之后。以下示例配置了一个 Between 路由断言
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 山区时间(丹佛)之前发出的任何请求。这对于维护窗口可能很有用。
Cookie 路由断言工厂
Cookie 路由断言工厂接受两个参数:cookie name 和一个 regexp(即 Java 正则表达式)。此断言匹配具有给定名称且其值与正则表达式匹配的 cookie。以下示例配置了一个 Cookie 路由断言工厂
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
此路由匹配具有名为 chocolate 的 cookie 且其值与 ch.p 正则表达式匹配的请求。
Header 路由断言工厂
Header 路由断言工厂接受两个参数:header 和一个 regexp(即 Java 正则表达式)。此断言匹配具有给定名称且其值与正则表达式匹配的请求头。以下示例配置了一个 Header 路由断言
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
如果请求具有名为 X-Request-Id 的请求头,且其值与 \d+ 正则表达式匹配(即,其值为一个或多个数字),则此路由匹配。
Host 路由断言工厂
Host 路由断言工厂接受一个参数:主机名 patterns 列表。模式是一个 Ant 风格的模式,以 . 作为分隔符。此断言匹配与模式匹配的 Host 请求头。以下示例配置了一个 Host 路由断言
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
也支持 URI 模板变量(例如 {sub}.myhost.org)。
如果请求具有 Host 请求头,且其值为 www.somehost.org 或 beta.somehost.org 或 www.anotherhost.org,则此路由匹配。
此断言将 URI 模板变量(例如前面示例中定义的 sub)提取为名称和值的映射,并将其放在 ServerWebExchange.getAttributes() 中,键由 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE 定义。这些值随后可用于 GatewayFilter 工厂
Method 路由断言工厂
Method 路由断言工厂接受一个 methods 参数,该参数是一个或多个 HTTP 方法参数,用于匹配。以下示例配置了一个 Method 路由断言
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
如果请求方法为 GET 或 POST,则此路由匹配。
Path 路由断言工厂
Path 路由断言工厂接受两个参数:一个 Spring PathMatcher patterns 列表和一个可选的 matchTrailingSlash 标志(默认为 true)。以下示例配置了一个 Path 路由断言
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)以方便访问这些变量。以下示例展示了如何使用 get 方法
Map<String, String> uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");
Query 路由断言工厂
Query 路由断言工厂接受两个参数:一个必需的 param 和一个可选的 regexp(即 Java 正则表达式)。以下示例配置了一个 Query 路由断言
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
如果请求包含 green 查询参数,则前面的路由匹配。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
如果请求包含 red 查询参数,且其值匹配 gree. 正则表达式,则前面的路由匹配,因此 green 和 greet 将匹配。
RemoteAddr 路由断言工厂
RemoteAddr 路由断言工厂接受一个 sources 列表(最小长度为 1),这些源是 CIDR 表示法(IPv4 或 IPv6)字符串,例如 192.168.0.1/16(其中 192.168.0.1 是 IP 地址,16 是子网掩码)。以下示例配置了一个 RemoteAddr 路由断言
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 附带一个非默认的远程地址解析器,该解析器基于 X-Forwarded-For 请求头,即 XForwardedRemoteAddressResolver。
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。
考虑以下请求头值
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
以下 maxTrustedIndex 值产生以下远程地址
maxTrustedIndex |
结果 |
|---|---|
[ |
(无效,初始化时发生 |
1 |
0.0.0.3 |
2 |
0.0.0.2 |
3 |
0.0.0.1 |
[4, |
0.0.0.1 |
以下示例展示了如何使用 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")
)
Weight 路由断言工厂
Weight 路由断言工厂接受两个参数:group 和 weight(一个整数)。权重按组计算。以下示例配置了一个 Weight 路由断言
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
XForwarded Remote Addr 路由断言工厂
XForwarded Remote Addr 路由断言工厂接受一个 sources 列表(最小长度为 1),这些源是 CIDR 表示法(IPv4 或 IPv6)字符串,例如 192.168.0.1/16(其中 192.168.0.1 是 IP 地址,16 是子网掩码)。
此路由断言允许根据 X-Forwarded-For HTTP 请求头过滤请求。
这可以与负载均衡器或 Web 应用程序防火墙等反向代理一起使用,其中只有当请求来自这些反向代理使用的受信任 IP 地址列表时才允许请求。
以下示例配置了一个 XForwardedRemoteAddr 路由断言
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
如果 X-Forwarded-For 请求头包含例如 192.168.1.10,则此路由匹配。