Initial gateway-request-predicates.adoc docs

This commit is contained in:
sgibb
2023-12-04 17:57:34 -05:00
parent e5281d6658
commit 7a419c8e5e

View File

@@ -1,4 +1,589 @@
[[gateway-request-predicates]]
= Gateway Request Predicates
TODO
Spring Cloud Gateway MVC matches routes as part of the Spring WebMvc.fn `HandlerMapping` infrastructure.
Spring Cloud Gateway reuses many https://docs.spring.io/spring-framework/reference/web/webmvc-functional.html#webmvc-fn-predicates[`RequestPredicate`] implementations from WebMvc.fn and includes other custom `RequestPredicate` implementations
All of these predicates match on different attributes of the HTTP request.
You can combine multiple route predicate factories with the `RequestPredicate.and()` and `RequestPredicate.or()` methods.
[[after-request-predicate]]
== The After Request Predicate
The `After` route predicate factory takes one parameter, a `datetime` (which is a java `ZonedDateTime`).
This predicate matches requests that happen after the specified datetime.
The following example configures an after route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
----
.GatewaySampleApplication.java
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.after;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsAfter() {
return route("after_route")
.route(after(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
}
}
----
This route matches any request made after Jan 20, 2017 17:42 Mountain Time (Denver).
[[before-request-predicate]]
== The Before Request Predicate
The `Before` route predicate factory takes one parameter, a `datetime` (which is a java `ZonedDateTime`).
This predicate matches requests that happen before the specified `datetime`.
The following example configures a before route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
----
.GatewaySampleApplication.java
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.before;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsBefore() {
return route("before_route")
.route(before(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
}
}
----
This route matches any request made before Jan 20, 2017 17:42 Mountain Time (Denver).
[[between-request-predicate]]
== The Between Request Predicate
The `Between` route predicate factory takes two parameters, `datetime1` and `datetime2`
which are java `ZonedDateTime` objects.
This predicate matches requests that happen after `datetime1` and before `datetime2`.
The `datetime2` parameter must be after `datetime1`.
The following example configures a between route predicate:
.application.yml
[source,yaml]
----
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]
----
.GatewaySampleApplication.java
[source,java]
----
import java.time.ZonedDateTime;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.between;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsBetween() {
return route("between_route")
.route(between(ZonedDateTime.parse("2017-01-20T17:42:47.789-07:00[America/Denver]"), ZonedDateTime.parse("2017-01-21T17:42:47.789-07:00[America/Denver]")), http("https://example.org"))
.build();
}
}
----
This route matches any request made after Jan 20, 2017 17:42 Mountain Time (Denver) and before Jan 21, 2017 17:42 Mountain Time (Denver).
This could be useful for maintenance windows.
[[cookie-request-predicate]]
== The Cookie Request Predicate
The `Cookie` route predicate factory takes two parameters, the cookie `name` and a `regexp` (which is a Java regular expression).
This predicate matches cookies that have the given name and whose values match the regular expression.
The following example configures a cookie route predicate factory:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
----
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.between;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCookie() {
return route("cookie_route")
.route(cookie("chocolate", "ch.p"), http("https://example.org"))
.build();
}
}
----
This route matches requests that have a cookie named `chocolate` whose value matches the `ch.p` regular expression.
[[header-request-predicate]]
== The Header Request Predicate
The `Header` route predicate factory takes two parameters, the `header` and a `regexp` (which is a Java regular expression).
This predicate matches with a header that has the given name whose value matches the regular expression.
The following example configures a header route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
----
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.header;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCookie() {
return route("cookie_route")
.route(header("X-Request-Id", "\\d+"), http("https://example.org"))
.build();
}
}
----
This route matches if the request has a header named `X-Request-Id` whose value matches the `\d+` regular expression (that is, it has a value of one or more digits).
[[host-request-predicate]]
== The Host Request Predicate
The `Host` route predicate factory takes one parameter: a list of host name `patterns`.
The pattern is an Ant-style pattern with `.` as the separator.
This predicates matches the `Host` header that matches the pattern.
The following example configures a host route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
----
.GatewaySampleApplication.java
[source,java]
----
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsHost() {
return route("host_route")
.route(host("**.somehost.org", "**.anotherhost.org"), http("https://example.org"))
.build();
}
}
----
URI template variables (such as `\{sub}.myhost.org`) are supported as well.
This route matches if the request has a `Host` header with a value of `www.somehost.org` or `beta.somehost.org` or `www.anotherhost.org`.
This predicate extracts the URI template variables (such as `sub`, defined in the preceding example) as a map of names and values and places it in the `ServerRequest.attributes()` with a key defined in `MvcUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE`.
// TODO: figure out link to gateway-handler-filter-functions
Those values are then available for use by Gateway Handler Filter Functions.
[[method-request-predicate]]
== The Method Request Predicate
The `Method` Request Predicate takes a `methods` argument which is one or more parameters: the HTTP methods to match.
The following example configures a method route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
----
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsMethod() {
return route("method_route")
.route(method(HttpMethod.GET, HttpMethod.POST), http("https://example.org"))
.build();
}
}
----
This route matches if the request method was a `GET` or a `POST`.
`GatewayRequestPredicates.method` is a simple alias for https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/function/RequestPredicates.html#methods(org.springframework.http.HttpMethod...)[`RequestPredicates.methods`]. Also, the `RouterFunctions.Builder` API includes convenience methods that combine the `method` and `path` `RequestPredicates`.
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.methods;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsMethod() {
return route("method_route")
.GET("/mypath", http("https://example.org"))
.build();
}
}
----
This route matches if the request method was a `GET` and the path was `/mypath`.
[[path-request-predicate]]
== The Path Request Predicate
The `Path` Request Predicate takes two parameters: a list of Spring `PathPattern` `patterns`.
// and an optional flag called `matchTrailingSlash` (defaults to `true`).
This Request Predicate uses https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/function/RequestPredicates.html#path(java.lang.String)[`RequestPredicates.path()`] as the underlying implementation.
The following example configures a path route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
----
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsPath() {
return route("path_route")
.route(path("/red/{segment}", "/blue/{segment}"), http("https://example.org"))
.build();
}
}
----
This route matches if the request path was, for example: `/red/1` or `/red/1/` or `/red/blue` or `/blue/green`.
//If `matchTrailingSlash` is set to `false`, then request path `/red/1/` will not be matched.
This predicate extracts the URI template variables (such as `segment`, defined in the preceding example) as a map of names and values and places it in the `ServerRequest.attributes()` with a key defined in `RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE`.
// TODO: figure out link
Those values are then available for use by Gateway Handler Filter Functions.
A utility method (called `get`) is available to make access to these variables easier.
The following example shows how to use the `get` method:
[source,java]
----
Map<String, Object> uriVariables = MvcUtils.getUriTemplateVariables(request);
String segment = uriVariables.get("segment");
----
////
TODO: query predicate
[[query-request-predicate]]
== The Query Request Predicate
The `Query` route predicate factory takes two parameters: a required `param` and an optional `regexp` (which is a Java regular expression).
The following example configures a query route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
----
The preceding route matches if the request contained a `green` query parameter.
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
----
The preceding route matches if the request contained a `red` query parameter whose value matched the `gree.` regexp, so `green` and `greet` would match.
TODO: remoteAddr predicate
[[remoteaddr-request-predicate]]
== The RemoteAddr Request Predicate
The `RemoteAddr` route predicate factory takes a list (min size 1) of `sources`, which are CIDR-notation (IPv4 or IPv6) strings, such as `192.168.0.1/16` (where `192.168.0.1` is an IP address and `16` is a subnet mask).
The following example configures a RemoteAddr route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
----
This route matches if the remote address of the request was, for example, `192.168.1.10`.
[[modifying-the-way-remote-addresses-are-resolved]]
=== Modifying the Way Remote Addresses Are Resolved
By default, the RemoteAddr route predicate factory uses the remote address from the incoming request.
This may not match the actual client IP address if Spring Cloud Gateway sits behind a proxy layer.
You can customize the way that the remote address is resolved by setting a custom `RemoteAddressResolver`.
Spring Cloud Gateway comes with one non-default remote address resolver that is based off of the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For[X-Forwarded-For header], `XForwardedRemoteAddressResolver`.
`XForwardedRemoteAddressResolver` has two static constructor methods, which take different approaches to security:
* `XForwardedRemoteAddressResolver::trustAll` returns a `RemoteAddressResolver` that always takes the first IP address found in the `X-Forwarded-For` header.
This approach is vulnerable to spoofing, as a malicious client could set an initial value for the `X-Forwarded-For`, which would be accepted by the resolver.
* `XForwardedRemoteAddressResolver::maxTrustedIndex` takes an index that correlates to the number of trusted infrastructure running in front of Spring Cloud Gateway.
If Spring Cloud Gateway is, for example only accessible through HAProxy, then a value of 1 should be used.
If two hops of trusted infrastructure are required before Spring Cloud Gateway is accessible, then a value of 2 should be used.
Consider the following header value:
[source]
----
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
----
The following `maxTrustedIndex` values yield the following remote addresses:
[options="header"]
|===
|`maxTrustedIndex` | result
|[`Integer.MIN_VALUE`,0] | (invalid, `IllegalArgumentException` during initialization)
|1 | 0.0.0.3
|2 | 0.0.0.2
|3 | 0.0.0.1
|[4, `Integer.MAX_VALUE`] | 0.0.0.1
|===
[[gateway-route-filters]]
The following example shows how to achieve the same configuration with Java:
.GatewayConfig.java
[source,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-request-predicate]]
== The Weight Request Predicate
The `Weight` route predicate factory takes two arguments: `group` and `weight` (an int). The weights are calculated per group.
The following example configures a weight route predicate:
.application.yml
[source,yaml]
----
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
----
.GatewaySampleApplication.java
[source,java]
----
import org.springframework.http.HttpMethod;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.method;
@Configuration
class RouteConfiguration {
@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsWeights() {
return route("weight_high")
.route(weight("group1", 8).and(path("/**")), http("https://weighthigh.org"))
.build().and(
route("weight_low")
.route(weight("group1", 2).and(path("/**")), http("https://weightlow.org"))
.build());
}
}
----
This route would forward ~80% of traffic to https://weighthigh.org and ~20% of traffic to https://weightlow.org
////
TODO: XForwardedRemoteAddr predicate
[[xforwarded-remote-addr-request-predicate]]
== The XForwarded Remote Addr Request Predicate
The `XForwarded Remote Addr` route predicate factory takes a list (min size 1) of `sources`, which are CIDR-notation (IPv4 or IPv6) strings, such as `192.168.0.1/16` (where `192.168.0.1` is an IP address and `16` is a subnet mask).
This route predicate allows requests to be filtered based on the `X-Forwarded-For` HTTP header.
This can be used with reverse proxies such as load balancers or web application firewalls where
the request should only be allowed if it comes from a trusted list of IP addresses used by those
reverse proxies.
The following example configures a XForwardedRemoteAddr route predicate:
.application.yml
[source,yaml]
----
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
----
This route matches if the `X-Forwarded-For` header contains, for example, `192.168.1.10`.
////