天天看點

【轉載】Spring Cloud Gateway-過濾器工廠詳解(GatewayFilter Factories)

TIPS

本文基于 ​

​Spring Cloud Greenwich SR2​

​ ,理論支援 ​

​Spring Cloud Greenwich SR1​

​ ,其中的新特性标注出來了。

這一節來探讨Spring Cloud Gateway内置的Filter工廠。包括:

1 AddRequestHeader GatewayFilter Factory

2 AddRequestParameter GatewayFilter Factory

3 AddResponseHeader GatewayFilter Factory

4 DedupeResponseHeader GatewayFilter Factory

5 Hystrix GatewayFilter Factory

6 FallbackHeaders GatewayFilter Factory

7 PrefixPath GatewayFilter Factory

8 PreserveHostHeader GatewayFilter Factory

9 RequestRateLimiter GatewayFilter Factory

10 RedirectTo GatewayFilter Factory

11 RemoveHopByHopHeadersFilter GatewayFilter Factory

12 RemoveRequestHeader GatewayFilter Factory

13 RemoveResponseHeader GatewayFilter Factory

14 RewritePath GatewayFilter Factory

15 RewriteResponseHeader GatewayFilter Factory

16 SaveSession GatewayFilter Factory

17 SecureHeaders GatewayFilter Factory

18 SetPath GatewayFilter Factory

19 SetResponseHeader GatewayFilter Factory

20 SetStatus GatewayFilter Factory

21 StripPrefix GatewayFilter Factory

22 Retry GatewayFilter Factory

23 RequestSize GatewayFilter Factory

24 Modify Request Body GatewayFilter Factory

25 Modify Response Body GatewayFilter Factory

26 Default Filters

技巧

  • 斷點打在 

    org.springframework.cloud.gateway.filter.NettyRoutingFilter#filter

     ,就可以調試Gateway轉發的具體細節了。
  • 添加如下配置,可觀察到一些請求細節:

    logging: level: org.springframework.cloud.gateway: trace org.springframework.http.server.reactive: debug org.springframework.web.reactive: debug reactor.ipc.netty: debug

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
      

為原始請求添加名為 ​

​X-Request-Foo​

​ ,值為 ​

​Bar​

​ 的請求頭。

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=foo, bar
      

為原始請求添加請求參數 ​

​foo=bar​

spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar
      

添加名為 ​

​X-Request-Foo​

​Bar​

​ 的響應頭。

Spring Cloud Greenwich SR2提供的新特性,低于這個版本無法使用。

強烈建議閱讀一下類org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory上的注釋,比官方文檔寫得還好。

spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST
      

剔除重複的響應頭。

舉個例子:

我們在Gateway以及微服務上都設定了CORS(解決跨域)header,如果不做任何配置,請求 -> 網關 -> 微服務,獲得的響應就是這樣的:

Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://musk.mars, https://musk.mars
      

也就是Header重複了。要想把這兩個Header去重,隻需設定成如下即可。

filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
      

也就是說,想要去重的Header如果有多個,用空格分隔即可;

去重政策:

RETAIN_FIRST: 預設值,保留第一個值
RETAIN_LAST: 保留最後一個值
RETAIN_UNIQUE: 保留所有唯一值,以它們第一次出現的順序保留
      

Hystrix是Spring Cloud第一代中的容錯元件,不過已經進入維護模式(相關文章:​​Spring Cloud Netflix項目進入維護模式之我見​​ ),未來,Hystrix會被Spring Cloud移除掉,取而代之的是Alibaba Sentinel/Resilience4J。

是以本文不做詳細探讨了,但Gateway整合Hystrix其實包含了很多姿勢。請感興趣的同學自行前往官方文檔了解詳情:​

​https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#hystrix​

​ 。
spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: https://example.org
        filters:
        - Hystrix=myCommandName

      

也是對Hystrix的支援,不做詳細探讨了,請感興趣的同學自行前往官方文檔了解詳情:​

​https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#fallback-headers​

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header

      

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath

      

為比對的路由添加字首。例如:通路​

​${GATEWAY_URL}/hello​

​ 會轉發到​

​https://example.org/mypath/hello​

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader

      

如果不設定,那麼名為 ​

​Host​

​ 的Header由Http Client控制;如果設定了,那麼會設定一個請求屬性(preserveHostHeader=true),路由過濾器會檢查進而去判斷是否要發送原始的、名為Host的Header。

在視訊Spring Cloud Gateway一章,限流一節會詳細講解。也可閱讀官方文檔 ​

​https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_requestratelimiter_gatewayfilter_factory​

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20

      

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
# 配置成HTTP狀态碼, URL的形式
        - RedirectTo=302, http://www.itmuch.com

      
  • HTTP狀态碼應該是HTTP狀态碼300序列,例如301
  • URL必須是合法的URL,并且該值會作為名為 ​

    ​Location​

    ​ 的Header。

上面配置表達的意思是: ​

​${GATEWAY_URL}/hello​

​ 會重定向到 ​

​https://ecme.org/hello​

​ ,并且攜帶一個 ​

​Location:http://www.itmuch.com​

spring.cloud.gateway.filter.remove-hop-by-hop.headers: Connection,Keep-Alive

      

移除轉發請求的Header,多個用 ​

​,​

​ 分隔。預設情況下,移除如下Header。這些Header是由 ​​IETF​​ 組織規定的。

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

      

為原始請求删除名為 ​

​X-Request-Foo​

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

      

删除名為 ​

​X-Request-Foo​

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
# 配置成原始路徑正則, 重寫後的路徑的正則
        - RewritePath=/foo/(?<segment>.*), /$\{segment}

      

重寫請求路徑。如上配置,通路 ​

​/foo/bar​

​ 會将路徑改為​

​/bar​

​ 再轉發,也就是會轉發到 ​

​https://example.org/bar​

​ 。需要注意的是,由于YAML文法,需用​

​$\​

​ 替換 ​

​$​

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://example.org
        filters:
        - RewriteResponseHeader=X-Response-Foo, password=[^&]+, password=***

      

如果名為 ​

​X-Response-Foo​

​ 的響應頭的内容是​

​/42?user=ford&password=omg!what&flag=true​

​,則會被修改為​

​/42?user=ford&password=***&flag=true​

​。

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

      

在轉發到後端微服務請求之前,強制執行 ​

​WebSession::save​

​ 操作。用在那種像 ​

​Spring Session​

​ 延遲資料存儲(筆者注:資料不是立刻持久化)的,并希望在請求轉發前確定session狀态儲存情況。

如果你将​

​Spring Secutiry​

​于​

​Spring Session​

​內建使用,并想確定安全資訊都傳到下遊機器,你就需要配置這個filter。

添加一系列起安全作用的響應頭。Spring Cloud Gateway參考了這篇部落格的建議:​

​https://blog.appcanary.com/2017/http-security-headers.html​

預設會添加如下Header(包括值):

  • ​X-Xss-Protection:1; mode=block​

  • ​Strict-Transport-Security:max-age=631138519​

  • ​X-Frame-Options:DENY​

  • ​X-Content-Type-Options:nosniff​

  • ​Referrer-Policy:no-referrer​

  • ​Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'​

  • ​X-Download-Options:noopen​

  • ​X-Permitted-Cross-Domain-Policies:none​

如果你想修改這些Header的值,可使用如下配置:

字首:​

​spring.cloud.gateway.filter.secure-headers​

上面的header對應的字尾:

  • ​xss-protection-header​

  • ​strict-transport-security​

  • ​frame-options​

  • ​content-type-options​

  • ​referrer-policy​

  • ​content-security-policy​

  • ​download-options​

  • ​permitted-cross-domain-policies​

例如:​

​spring.cloud.gateway.filter.secure-headers.xss-protection-header: 你想要的值​

如果想禁用某些Header,可使用如下配置:​

​spring.cloud.gateway.filter.secure-headers.disable​

​ ,多個用 ​

​,​

​ 分隔。例如:​

​spring.cloud.gateway.filter.secure-headers.disable=frame-options,download-options​

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}

      

采用路徑​

​template​

​參數,通過請求路徑的片段的模闆化,來達到操作修改路徑的母的,運作多個路徑片段模闆化。

如上配置,通路​

​${GATEWAY_PATH}/foo/bar​

​ ,則對于後端微服務的路徑會修改為 ​

​/bar​

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: http://example.org
        filters:
        - SetResponseHeader=X-Response-Foo, Bar

      

如果後端服務響應帶有名為 ​

​X-Response-Foo​

​ 的響應頭,則将值改為替換成 ​

​Bar​

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: http://example.org
        filters:
        - SetStatus=401

      

修改響應的狀态碼,值可以是數字,也可以是字元串。但一定要是Spring ​

​HttpStatus​

​ 枚舉類中的值。如上配置,兩種方式都可以傳回HTTP狀态碼401。

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

      

數字表示要截斷的路徑的數量。如上配置,如果請求的路徑為 ​

​/name/bar/foo​

​ ,則路徑會修改為​

​/foo​

​ ,也就是會截斷2個路徑。

spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY

      

針對不同的響應做重試,可配置如下參數:

  • ​retries​

    ​: 重試次數
  • ​statuses​

    ​: 需要重試的狀态碼,取值在 ​

    ​org.springframework.http.HttpStatus​

    ​ 中
  • ​methods​

    ​: 需要重試的請求方法,取值在 ​

    ​org.springframework.http.HttpMethod​

  • ​series​

    ​: HTTP狀态碼系列,取值在 ​

    ​org.springframework.http.HttpStatus.Series​

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
      - Path=/upload
      filters:
      - name: RequestSize
        args:
# 機關位元組
          maxSize: 5000000

      

為後端服務設定收到的最大請求包大小。如果請求大小超過設定的值,則傳回 ​

​413 Payload Too Large​

​ 。預設值是5M

該過濾器處于 ​

​BETA​

​ 狀态,未來API可能會變化,生産環境請慎用。

可用于在Gateway将請求發送給後端微服務之前,修改請求體内容。該過濾器隻能通過代碼配置,不支援在配置檔案設定。示例:

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}

static class Hello {
String message;

public Hello() { }

public Hello(String message) {
this.message = message;
    }

public String getMessage() {
return message;
    }

public void setMessage(String message) {
this.message = message;
    }
}

      

​BETA​

可用于修改響應體内容。該過濾器隻能通過代碼配置,不支援在配置檔案設定。示例:

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)
        .build();
}

      

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      - PrefixPath=/httpbin

      

如果你想為所有路由添加過濾器,可使用該屬性。

繼續閱讀