- [spring cloud gateway](#spring-cloud-gateway)
- [项目引入spring cloud gateway](#项目引入spring-cloud-gateway)
- [Spring Cloud gateway核心概念](#spring-cloud-gateway核心概念)
- [Spring Cloud Gateway如何工作](#spring-cloud-gateway如何工作)
- [配置route predicate factories和gateway filter factories](#配置route-predicate-factories和gateway-filter-factories)
- [shortcut配置方式](#shortcut配置方式)
- [Route Predicate Factories](#route-predicate-factories)
- [After Route Predicate Factory](#after-route-predicate-factory)
- [Before Route Predicate Factory](#before-route-predicate-factory)
- [Between Route Predicate Factory](#between-route-predicate-factory)
- [Cookie Route Predicate Factory](#cookie-route-predicate-factory)
- [Header Route Predicate Factory](#header-route-predicate-factory)
- [Host Route Predicate Factory](#host-route-predicate-factory)
- [Method Route Predicate Factory](#method-route-predicate-factory)
- [Path Route Predicate Factory](#path-route-predicate-factory)
- [Query Route Predicate Factory](#query-route-predicate-factory)
- [RemoteAddr Route Predicate Factory](#remoteaddr-route-predicate-factory)
- [修改Remote Addr的解析方式](#修改remote-addr的解析方式)
- [Weight Route Predicate Factory](#weight-route-predicate-factory)
- [XForwarded Remote Addr Route Predicate](#xforwarded-remote-addr-route-predicate)
- [GatewayFilter Factory](#gatewayfilter-factory)
- [AddRequestHeader GatewayFilter Factory](#addrequestheader-gatewayfilter-factory)
- [AddRequestHeadersIfNotPresent GatewayFilter Factory](#addrequestheadersifnotpresent-gatewayfilter-factory)
- [AddRequestParameter GatewayFilter Factory](#addrequestparameter-gatewayfilter-factory)
- [AddResponseHeader GatewayFilter Factory](#addresponseheader-gatewayfilter-factory)
- [CircuitBreaker GatewayFilter Factory](#circuitbreaker-gatewayfilter-factory)
- [CacheRequestBody GatewayFilter Factory](#cacherequestbody-gatewayfilter-factory)
- [DedupeResponseHeader GatewayFilter Factory](#deduperesponseheader-gatewayfilter-factory)
- [默认filters](#默认filters)
- [Global Filters](#global-filters)
- [组合Global Filter和Gateway Filter](#组合global-filter和gateway-filter)
# spring cloud gateway
## 项目引入spring cloud gateway
如果要在项目中引入spring cloud gateway,可以在项目pom文件中添加如下依赖:
```xml
org.springframework.cloud
spring-cloud-starter-gateway
```
> 如果已经在项目中引入了spring-cloud-starter-gateway的依赖,但是不想启用gateway,可以在配置文件中指定`spring.cloud.gateway.enabled=false`
> Spring Cloud gateway在运行时需要netty作为运行时环境,并且采用Spring Webflux构建,gateway在传统的基于servlet的环境中无法运行。
## Spring Cloud gateway核心概念
- Route:gateway的基本构建单元,Route由一个id、一个目标uri,predicate集合、filter集合组成。如果聚合predicate为true,那么route被匹配到
- Predicate:Predicate的输入是` Spring Framework ServerWebExchange`,通过该输入,可以匹配http请求中的任何内容,如header或请求参数等
- Filter:其是GatewayFilter的实例,通过特定的factory构造。在filter中,可以在发送到下游请求之前或者之后对request和response进行修改。
## Spring Cloud Gateway如何工作
1. 客户端向spring cloud gateway发送请求,如果Gateway Handler Mapping将请求匹配到route,那么会向gateway web handler发送请求
2. gateway web handler通过filter chain对请求进行处理
## 配置route predicate factories和gateway filter factories
有两种方式来配置predicate和filter:shortcuts和全展开参数
### shortcut配置方式
```yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- Cookie=mycookie,mycookievalue
```
## Route Predicate Factories
Spring Cloud Gateway包含了许多内置的route predicate factories,所有的这些predicate都匹配到http请求的不同属性。可以通过逻辑运算符`and`将多个route predicate factory组合起来。
### After Route Predicate Factory
After Route Predicate Factory接收一个参数`datetime`,该参数为ZonedDateTime类型,该Predicate匹配发生在指定时间之后的请求。
```yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
```
该route匹配2017-01-20T17:42:47.789-07:00[America/Denver]该时间之后的任意请求
### Before Route Predicate Factory
Before route predicate factory接收一个`datetime`参数,参数为ZonedDateTime类型,该predicate匹配发生在某时间之前的请求。
```yml
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
```
该route匹配发生在2017-01-20T17:42:47.789-07:00[America/Denver]之前的任意请求
### Between Route Predicate Factory
Between Route Predicate Factory接收两个参数,都是ZonedDateTime类型,该predicate匹配发生在datetime1和datetime2之间的请求,其中第二个参数指定的时间必须位于第一个参数指定时间之后。如下指定了一个between predicate示例:
```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]
```
### Cookie Route Predicate Factory
Cookie Route Predicate Factory接收两个参数,`name`和`regexp`。该predicate匹配请求中含有指定名称,并且cookie值满足`regexp`正则表达式。如下展示了一个cookie predicate示例:
```xml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
```
上述cookie_route会匹配请求中含有name为chocolate的cookie,并且cookie值满足`ch.p`正则表达式的请求。
### Header Route Predicate Factory
Header Route Predicate Factory接收两个参数,`header`和`regexp`(regexp是正则表达式)。该predicate会匹配一个拥有指定名称header的请求,且header对应的值要满足regexp表达式。
如下示例显示了header route predicate的用法:
```yml
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
```
### Host Route Predicate Factory
Host Route Predicate Factory接收一个参数:一个pattern列表。pattern是ant-style并用`.`作为分隔符。该predicate会匹配`Host`header,示例如下所示:
```yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
```
URI template 变量例如`{sub}.myhost.org`也支持。
该route会匹配如下请求:headers中包含name为Host的header,并且Host header的值为`www.somehost.org`,`beta.somehost.org`,`www.anotherhost.org`等形式
### Method Route Predicate Factory
Method Route Predicate Factory接收一个`methods`参数,其指定了匹配的HTTP METHOD. 如下示例指定了一个method route predicate示例:
```yml
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
```
该predicate匹配method为GET或POST的任意请求。
### Path Route Predicate Factory
Path Route Predicate Factory接收两个参数,一个PathMatcher Pattern的列表,和一个可选的matchTrailingSlash(默认设置为true)。
如下实例配置了一条path route predicate:
```yml
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
```
上述配置的route能够匹配/red/1或/red/1/、/red/blue或/blue/green.
如果`matchTrailingSlash`被设置为false,那么/red/1/将不会被匹配到。
该predicate将URI template变量(例如segment)解析为name、value键值对并且放到ServerWebExchange.getAttributes()中。这些值可以被GatewayFilter factories使用。
可以通过如下的工具类方法来对这些变量进行访问,按如下示例所示:
```java
Map uriVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange);
String segment = uriVariables.get("segment");
```
### Query Route Predicate Factory
Query Route Predicate Factory接收两个参数:必填参数param和可选参数regexp,示例如下所示:
```yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
```
上述路由配置,匹配请求参数中含有green参数的请求
```yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=red, gree.
```
该route则是匹配请求参数含有red,并且其value满足`gree.`正则表达式的请求。
### RemoteAddr Route Predicate Factory
RemoteAddr route predicate factory接收一个`sources`列表(列表最小长度为1),列表元素为ipv4或ipv6字符串,例如`192.168.0.1/1`.
如下显示了一个RemoteAddr route predicate示例:
```yml
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
```
该route匹配如下请求:如果请求的remote addr满足表达式,例如`192.168.1.10`。
#### 修改Remote Addr的解析方式
默认情况下,RemoteAddr predicate factory的解析方式是使用请求的remote address。但是,如果Spring Cloud Gateway如果位于proxy layer之后,那么remote address匹配的不是实际的client ip。
可以通过自定义`RemoteAddressResolver`来自定义remote address的解析方式。Spring Cloud Gateway附带了一个非默认的remote address reslover,该reslover基于` X-Forwarded-For`header进行解析,该resolver是`XForwardedRemoteAddressResolver`。
XForwardedRemoteAddressResolver有两个静态的构造器方法,分别使用了两种不同的安全方式:
- `XForwardedRemoteAddressResolver::trustAll`:返回一个RemoteAddressResolver,会一直从`X-Forwarded-For`中取第一个ip address。该方法很容易收到欺骗,因为恶意客户端程序会设置`X-Forwarded-For`字段的初始值,而此时reslover会接收虚假设置的值。
- `XForwardedRemoteAddressResolver::maxTrustedIndex`:接收一个index,该index和Spring Cloud Gateway之前运行的受信任设施的数量相关。
如存在如下header:
```yml
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
```
如下maxTrustedIndex会产生的remote address将如下所示:
| maxTrustedIndex | result |
| :-: | :-: |
| [Integer.MIN_VALUE,0] | 该范围输入不合法 |
| 1 | 0.0.0.3 |
| 2 | 0.0.0.2 |
| 3 | 0.0.0.1 |
| [4, Integer.MAX_VALUE] | 0.0.0.1 |
如下展示了如何通过java代码来完成相同的配置:
```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 Route Predicate Factory
Weight Route Predicate Factory接收两个参数:`group`和`weight`,weight将对每个group进行计算。如下示例配置了一个Weight Route Predicate:
```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
```
该route将会把80%的负载转发到https://weighthigh.org,把20%的负载发送到https://weightlow.org
### XForwarded Remote Addr Route Predicate
XForwarded Remote Addr Route Predicate接收一个`sources`列表,列表元素为ipv4或ipv6地址,例如`192.168.0.1/16`。
该route predicate允许通过`X-Forwarded-For` header来过滤请求。
该predicate可以和反向代理服务器(通常用作负载均衡或web应用防火墙)一起使用,只有当请求来源于这些受信ip地址列表时,请求才会被允许访问。
```yml
spring:
cloud:
gateway:
routes:
- id: xforwarded_remoteaddr_route
uri: https://example.org
predicates:
- XForwardedRemoteAddr=192.168.1.1/24
```
上述route,只有当`X-Forwarded-For` header中包含例如`192.168.1.10`的ip时,请求才会被匹配。
## GatewayFilter Factory
Route filters 允许对进入的请求和外出的响应进行修改,Route Filter作用域为特定的route。Spring Cloud Gateway有许多内置的route filter。
### AddRequestHeader GatewayFilter Factory
AddRequestHeader GatewayFilter Factory接收一个name和value参数,如下示例配置了一个AddRequestHeader GatewayFilter:
```yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
```
上述配置对所有匹配的请求在下游请求头中添加了`X-Request-red:blue`header。
AddRequestHeader可以使用用于匹配path或host的URI变量。URI变量可以在value中使用,并且在运行时自动拓展,如下示例配置了一个使用URI变量的`AddRequestHeader GatewayFilter`:
```yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeader=X-Request-Red, Blue-{segment}
```
### AddRequestHeadersIfNotPresent GatewayFilter Factory
AddRequestHeadersIfNotPresent GatewayFilter接收一个name和value对集合,name和value之间通过`:`进行分隔,如下示例配置了一个filter:
```yml
spring:
cloud:
gateway:
routes:
- id: add_request_headers_route
uri: https://example.org
filters:
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green
```
上述配置会添加两个headers`X-Request-Color-1:blue`和`X-Request-Color-2:green`到下游请求的headers中。该操作和`AddRequestHeader`类似,但是AddRequestHeadersIfNotPresent只会当header不存在时才进行添加。如果该header存在,那么会保留该header的原有值。
> 如果要设置一个multi-valued header,可以使用该headerName多次,例如`AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-1:green`
`AddRequestHeadersIfNotPresent`也支持URI变量,如下配置了一个使用URI变量的Gateway Filter:
```yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeadersIfNotPresent=X-Request-Red:Blue-{segment}
```
### AddRequestParameter GatewayFilter Factory
AddRequestParameter GatewayFilter Factory接收一个name和value参数,如下示例配置了一个filter:
```xml
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
```
上述配置会向下游请求中加入请求参数。
AddRequestParameter GatewayFilter可以使用URI变量,使用URI变量的配置如下所示:
```yml
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
```
### AddResponseHeader GatewayFilter Factory
`AddResponseHeader GatewayFilter Factory接收一个name和value参数,如下配置了一个filter:
```yml
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
```
该filter添加了`X-Response-Red:Blue` header到下游响应的headers中。
`AddResponseHeader GatewayFilter`可以使用URI变量,如下实例展示了使用URI变量的配置:
```yml
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddResponseHeader=foo, bar-{segment}
```
### CircuitBreaker GatewayFilter Factory
CircuitBreaker GatewayFilter Factory使用了Spring Cloud CircuitBreaker API来将网关路由包装到断路器中。
为了启用Spring Cloud CircuitBreaker filter,需要将`spring-cloud-starter-circuitbreaker-reactor-resilience4j`放到classpath路径下,如下示例配置了filter:
```xml
spring:
cloud:
gateway:
routes:
- id: circuitbreaker_route
uri: https://example.org
filters:
- CircuitBreaker=myCircuitBreaker
```
### CacheRequestBody GatewayFilter Factory
有一些场景需要读取请求体,因为请求只能被读取一次,因此需要缓存请求体。可以使用CacheRequestBody filter来缓存请求体,在请求被发送到下游之前,并且可以从`exchange`属性中获取请求体。
```xml
spring:
cloud:
gateway:
routes:
- id: cache_request_body_route
uri: lb://downstream
predicates:
- Path=/downstream/**
filters:
- name: CacheRequestBody
args:
bodyClass: java.lang.String
```
上述配置获取请求体并且将请求体转为了String类型,后续可以通过`ServerWebExchange.getAttributes()`来获取,key为`ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR`.
### DedupeResponseHeader GatewayFilter Factory
DedupeResponseHeader GatewayFilter factory接收一个name参数和一个可选的strategy参数,name是一个用空格分隔的list,list元素为header name。如下配置了一个filter示例:
```yml
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
```
上述配置中移除了Access-Control-Allow-Credentials和Access-Control-Allow-Origin响应header中的重复值,当gateway cors逻辑和下游逻辑都添加了它们时。
DedupeResponseHeader还有一个可选的strategy参数,默认为RETAIN_FIRST,还可以是RETAIN_LAST和RETAIN_UNIQUE。
### 默认filters
如果想要添加一个filter并将其应用到所有的routes中,可以使用`spring.cloud.gateway.default-filters`,该属性可以接收一个filter列表。如下定义了一系列默认filter:
```yml
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
```
## Global Filters
GlobalFilter接口的签名和Gateway接口的签名一样。GlobalFilter会在满足条件后应用于所有的routes。
### 组合Global Filter和Gateway Filter
当请求匹配到一个route之后,filtering web handler将所有的GlobalFilter实例和route对应的Gateway Filter实例添加到一个filter chain中。该filter chain通过`org.springframework.core.Ordered`接口进行排序。filter chain中排序最靠前的filter在pre-phase中位于第一个,并且在post-phase中位于最后一个。
如下配置了一个filter chain:
```java
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
```