第二代網關GateWay搭建流程

Spring Cloud第二代網關GateWay是由純Netty開發,底層爲Reactor,WebFlux構建,不依賴任何Servlet容器,它不一樣於Zuul,使用的是異步IO,性能較Zuul提高1.6倍。搭建過程以下(本次搭建的爲子項目,主項目能夠參考Nacos搭建流程 )java

pomweb

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置文件正則表達式

server:
  port: 8040
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true

以上的意思不只是把本身給註冊到nacos,而且獲取nacos的全部註冊服務。redis

啓動網關項目,如今就能夠進行網絡路由了。訪問格式爲 ip:端口/服務註冊名/restfulapi-urlspring

比方說咱們如今有兩個微服務項目,一個爲user(端口8082),一個爲nacos(端口8081).編程

三大核心概念後端

  • Route(路由) Spring Cloud Gateway的基礎元素,可簡單理解成一條轉發的規則。包含:ID,目標的URL,Predicate集合以及Filter集合。
  • Predicate(謂詞) 即java.util.function.Predicate,Spring Cloud Gateway使用Predicate實現路由的匹配條件。這是一個能夠進行條件判斷的函數式接口,具體能夠參考本人博客Java函數式編程整理
  • Filter(過濾器) 修改請求以及響應。

因爲咱們使用了nacos來進行服務發現,因此咱們使用了以前的配置文件,但若是不使用服務發現,只作常規的轉發以下api

spring:
  cloud:
    gateway:
      routes:
        - id: some_route
          uri: http://www.baidu.com
          predicates:
            - Path=/user/1
          filtes:
            - AddRequestHeader=X-Request-Foo, Bar

這段配置的意思是說,當咱們請求/user/1的url的時候,會添加AddRequestHeader=X-Request-Foo, Bar過濾器作一些處理,而後路由到http://www.baidu.com。跨域

路由謂詞配置工廠瀏覽器

路由謂詞配置工廠由一整套謂詞來進行配置轉發的不一樣狀況。

謂詞工廠 備註
After 此謂詞匹配當前日期時間以後發生的請求。
Before 此謂詞匹配在當前日期時間以前發生的請求。
Between 此謂詞匹配datetime1以後和datetime2以前發生的請求。 datetime2參數必須在datetime1以後。
Cookie Cookie Route Predicate Factory有兩個參數,cookie名稱和正則表達式。此謂詞匹配具備給定名稱且值與正則表達式匹配的cookie。
Header Header Route Predicate Factory有兩個參數,標題名稱和正則表達式。與具備給定名稱且值與正則表達式匹配的標頭匹配。
Host Host Route Predicate Factory採用一個參數:主機名模式。該模式是一種Ant樣式模式「.」做爲分隔符。此謂詞匹配與模式匹配的Host標頭。
Method Method Route Predicate Factory採用一個參數:要匹配的HTTP方法。
Path 匹配請求的path
Query Query Route Predicate Factory有兩個參數:一個必需的參數和一個可選的正則表達式。
RemoteAddr RemoteAddr Route Predicate Factory採用CIDR符號(IPv4或IPv6)字符串的列表(最小值爲1),例如, 192.168.0.1/16(其中192.168.0.1是IP地址,16是子網掩碼)。

路由到指定URL

  • 通配

如今咱們去掉nacos的配置,不禁nacos來發現

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

此時訪問

將跳轉到

  • 謂詞After
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #在2019-12-14日20:26後容許該轉發
        - After=2019-12-14T20:26:15.667+08:00[Asia/Shanghai]
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

這裏表示在該時間後容許轉發,若是咱們將該時間設置爲

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #在2019-12-15日20:26後容許該轉發
        - After=2019-12-15T20:26:15.667+08:00[Asia/Shanghai]
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

則轉發失敗,返回404

咱們能夠經過如下方法來獲取這裏的時間設置

public class TimeTest {
    public static void main(String[] args) {
        System.out.println(ZonedDateTime.now());
    }
}

運行結果

2019-12-14T20:43:34.755+08:00[Asia/Shanghai]

  • 謂詞Before

如今咱們將上面的15號改成Before

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #在2019-12-15日20:26前容許該轉發
        - Before=2019-12-15T20:26:15.667+08:00[Asia/Shanghai]
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

此時就能夠正常轉發,而改爲14號則會失敗。

  • 謂詞Between
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #在2019-12-14日20:26到2019-12-15日20:26之間容許該轉發
        - Between=2019-12-14T20:26:15.667+08:00[Asia/Shanghai],2019-12-15T20:26:15.667+08:00[Asia/Shanghai]
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
  • 謂詞Cookie

咱們在user模塊增長一個帶cookie的Controller

@Slf4j
@RestController
public class CookieController {
    @GetMapping("/welcome")
    public Boolean handle(HttpServletRequest request,
                               HttpServletResponse response) throws Exception {
        Cookie cookie = new Cookie("test","value");
        cookie.setMaxAge(Integer.MAX_VALUE);
        response.addCookie(cookie);
        log.info("welcome");
        return true;
    }
}

此時咱們訪問該Controller爲

此時網關這邊配置爲

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有帶上Cookie名爲test,而且值符合正則value的cookie時,才容許被轉發
        - Cookie=test,value
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

  • 謂詞Header

如今咱們給user模塊添加一個Controller的方法

@GetMapping("/header")
public String header(@RequestHeader("item") String item) {
    return item;
}

咱們經過postman給該方法的訪問添加請求頭

在網關中的配置爲

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有帶上請求頭名爲item,而且值符合正則123.p,纔會轉發
        - Header=item,123.p
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

這裏正則.能夠匹配一個單字符

若是咱們在請求頭item中設置錯誤的字符則沒法轉發

  • 謂詞Host

要配置Host,咱們須要給服務器的hosts文件添加一個域名映射,固然在互聯網上須要一個域名來作DNS解析。

我這裏給本身的域名添加爲local.register.com

訪問user的find方法

給網關添加配置

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有帶上請求頭Host,且值匹配**.register.com:8040才能經過轉發
        - Host=**.register.com:8040
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

此時咱們經過網關訪問以下

  • 謂詞Method
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有當HTTP請求方法是GET時才能轉發
        - Method=GET
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
  • 謂詞Query

如今咱們給user模塊增長一個Controller方法

@GetMapping("/query")
public String query(@RequestParam("name") String name) {
    return name;
}

訪問以下

網關配置以下

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有當請求帶上參數名稱爲name時才能經過轉發
        - Query=name
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

若是不帶上該參數則沒法轉發,如

  • 謂詞RemoteAddr
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
      - id: gate
        uri: http://127.0.0.1:8082
        predicates:
        #由/user來匹配跳轉
        - Path=/user/**
        #只有當請求爲192.168.20.1/24網段(IP地址從192.168.20.1到192.168.20.254)纔會轉發
        - RemoteAddr=192.168.20.1/24
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

例如

可是使用127.0.0.1卻沒法訪問

如今咱們恢復nacos的服務發現

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

爲了跟不作任何配置相區別,咱們這裏謂詞Path寫了user-center

自定義路由謂詞工廠

假設如今咱們的一個API只有在上午9點到下午5點容許轉發

配置的文件以下

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        - TimeBetween=上午9:00,下午5:00
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1

因爲這個TimeBetween並非gateway默認的謂詞工廠,因此咱們須要本身來實現一個謂詞工廠,咱們先定義一個時間的配置類

@Data
public class TimeBetweenConfig {
    private LocalTime start;
    private LocalTime end;
}

而後自定義一個謂詞工廠類,該工廠類名稱必須以自定義謂詞開頭(這裏是TimeBetween),以RoutePredicateFactory結尾,並繼承AbstractRoutePredicateFactory抽象類

@Component
public class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenConfig>{
    public TimeBetweenRoutePredicateFactory() {
        super(TimeBetweenConfig.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) {
        LocalTime start = config.getStart();
        LocalTime end = config.getEnd();
        return exchange -> {
            LocalTime now = LocalTime.now();
            return now.isAfter(start) && now.isBefore(end);
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("start","end");
    }
}

內置過濾器工廠

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

  • AddRequestHeader
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.10.172:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #增長一個名稱爲X-Request-Foo,值爲Bar的請求頭
        - AddRequestHeader=X-Request-Foo,Bar

這裏須要注意的是新增的這個請求頭是轉發之後添加進去的,因此咱們請求網關的時候在瀏覽器中是找不到的,咱們可使用command+N(Windows中idea爲Ctrl+N)來查找NettyRoutingFilter類,而且在filter方法中設置斷點,由如下圖中能夠看到它是被添加進去了。

  • AddRequestParameter

因爲在user模塊中有這麼一個方法

@GetMapping("/query")
public String query(@RequestParam("name") String name) {
    return name;
}

因此咱們在網關配置時

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #增長一個名稱爲name,值爲locky的請求參數
        - AddRequestParameter=name,locky

因此咱們在網關中請求就能夠不寫參數,直接訪問

  • AddResponseHeader
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #增長一個名稱爲X-Response-Foo,值爲Bar的響應頭
        - AddResponseHeader=X-Response-Foo, Bar

  • DedupeResponseHeader

Spring Cloud Greenwich SR2提供的新特性,低於這個版本沒法使用。
它的主要做用是去重,例如

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        - Cookie=test,value
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #在Http響應報文頭中進行去重,去重目標爲跨域請求
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
  • Hystrix
Hystrix是Spring Cloud第一代中的容錯組件,不過已經進入維護模式將來,Hystrix會被Spring Cloud移除掉,取而代之的是Alibaba Sentinel/Resilience4J

此處不作具體設置了

  • FallbackHeaders

也是對Hystrix的支持,不作具體設置了

  • PrefixPath

爲匹配的路由添加前綴,咱們在user模塊的find添加一層訪問路徑

@GetMapping("/test/find")
@SuppressWarnings("unchecked")
public Result<User> findStr() {
    log.info("訪問成功");
    return Result.success(new User(1,"張三",23));
}

網關配置

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #跳轉後添加前綴/test
        - PrefixPath=/test

一致。

  • PreserveHostHeader

若是不設置,那麼名爲 Host 的Header由Http Client控制;若是設置了,那麼會設置一個請求屬性(preserveHostHeader=true),路由過濾器會檢查從而去判斷是否要發送原始的、名爲Host的Header。這裏主要是經過網關是否向代理服務器轉發請求頭中的Host屬性。

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #轉發客戶端的請求報文頭Host給後端代理服務器
        - PreserveHostHeader
  • RequestRateLimiter

Gateway自帶的限流服務,但後續咱們會整合Gateway和Sentinel來進行限流和熔斷。

  • RedirectTo

轉發到後端服務後再重定向到一個url.

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #轉發到Path,而且攜帶一個http://www.baidu.com到Location的響應頭
        - RedirectTo=302,http://www.baidu.com

從以上圖中能夠看出,其實咱們請求的是http://127.0.0.1:8040/user-center/find,可是被重定向到了百度。這裏HTTP狀態碼應該是HTTP狀態碼300序列,例如301.302,具體狀態碼能夠參考HTTP協議整理

  • RemoveHopByHopHeadersFilter

移除轉發請求的Header,多個用","分隔。默認狀況下移除以下Header。

  1. Connection
  2. Keep-Alive
  3. Proxy-Authenticate
  4. Proxy-Authorization
  5. TE
  6. Trailer
  7. Transfer-Encoding
  8. Upgrade
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
      filter:
        #移除轉發請求
        remove-hop-by-hop:
          headers: Keep-Alive,Connection
  • RemoveRequestHeader

移除原始請求

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #移除原始請求頭X-Request-Foo
        - RemoveRequestHeader=X-Request-Foo

spring cloud zuul網關的做用 可知,在跨域轉發中,咱們須要移除這些請求頭

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #移除原始跨域請求頭
        - RemoveRequestHeader=Access-Control-Allow-Origin
      filter:
        #移除轉發請求
        remove-hop-by-hop:
          headers: Access-Control-Allow-Credentials,Access-Control-Allow-Origin,Vary,X-Frame-Options,token
  • RemoveResponseHeader

移除響應頭

咱們在user中添加一個Controller方法

@GetMapping("/addhead")
public String addHeader(HttpServletRequest request, HttpServletResponse response) {
    response.addHeader("X-Response-Foo","Foo");
    return "header";
}

網關配置

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #移除響應頭X-Response-Foo
        - RemoveResponseHeader=X-Response-Foo

經過網關轉發,咱們能夠看到無此X-Response-Foo的響應頭。

  • RewritePath

重寫請求路徑

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #配置成原始路徑正則, 重寫後的路徑的正則
        - RewritePath=/user-center/(?<segment>.*), /$\{segment}

以上配置會將/user-center/find變成/find再轉發

直接訪問user

網關請求的

  • RewriteResponseHeader

重寫響應頭部份內容,根據正則來修改

以前在user中有一個Controller方法

@GetMapping("/addhead")
public String addHeader(HttpServletRequest request, HttpServletResponse response) {
    response.addHeader("X-Response-Foo","Foo");
    return "header";
}
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #重寫響應頭X-Response-Foo的值Foo爲dee,內容可根據正則匹配
        - RewriteResponseHeader=X-Response-Foo,Foo,dee

訪問user的/addhead,X-Response-Foo響應頭的值爲Foo.

經過網關訪問/addhead,X-Response-Foo響應頭的值爲dee

  • SaveSession

在轉發到後端微服務請求以前,強制執行 WebSession::save 操做。用在那種像 Spring Session 延遲數據存儲(數據不是馬上持久化)的,並但願在請求轉發前確保session狀態保存狀況。

如今咱們對user進行共享Session的配置,添加依賴

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.session</groupId>
   <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.9.0</version>
</dependency>

添加配置

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: xxxxx
    timeout: 10000
    lettuce:
      pool:
        min-idle: 0
        max-idle: 8
        max-active: 8
        max-wait: -1

在SpringBoot開啓共享Session

@EnableRedisHttpSession
@SpringBootApplication
public class UserApplication {

   public static void main(String[] args) {
      SpringApplication.run(UserApplication.class, args);
   }

}

在user中添加以下Controller

@RestController
public class SessionController {
    @GetMapping("/first")
    public Map<String,Object> firstResp(HttpServletRequest request, HttpServletResponse response) {
        Map<String,Object> map = new HashMap<>();
        request.getSession().setAttribute("request Url",request.getRequestURL());
        map.put("request Url",request.getRequestURL());
        return map;
    }

    @GetMapping("/sessions")
    public Object sessions(HttpServletRequest request,HttpServletResponse response) {
        Map<String,Object> map = new HashMap<>();
        map.put("SessionId",request.getSession().getId());
        map.put("message",request.getSession().getAttribute("request Url"));
        return map;
    }
}

咱們啓動兩個user實例,一個端口號爲8082,一個爲8083,訪問以下

咱們能夠看到除了存入Session的RequestURL不一樣之外,他們的SessionId是相同的,說明這裏是一個共享的Session。

咱們不對網關配置作修改,則咱們經過網關訪問

在訪問first的時候,它會負載均衡這兩個實例,但咱們能夠看到他在Session中存儲的是內網的IP,而不是127.0.0.1。

在網關作以下配置

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #請求轉發前保存Session
        - SaveSession

目前未測出有什麼太大的做用。反而會破壞Session的數據。因此不建議增長該設置。

  • SecureHeaders

添加一系列起安全做用的響應頭。

默認會添加以下Header(包括值):

  1. X-Xss-Protection:1; mode=block 防XSS攻擊設置,0: 表示關閉瀏覽器的XSS防禦機制;1:刪除檢測到的惡意代碼, 若是響應報文中沒有看到X-XSS-Protection 字段,那麼瀏覽器就認爲X-XSS-Protection配置爲1,這是瀏覽器的默認設置.1; mode=block:若是檢測到惡意代碼,在不渲染惡意代碼.
  2. Strict-Transport-Security:max-age=631138519 一個網站接受一個HTTP的請求,而後跳轉到HTTPS,用戶可能在開始跳轉前,經過沒有加密的方式和服務器對話,好比,用戶輸入http://foo.com或者直接foo.com。這樣存在中間人攻擊潛在威脅,跳轉過程可能被惡意網站利用來直接接觸用戶信息,而不是原來的加密信息。網站經過HTTP Strict Transport Security通知瀏覽器,這個網站禁止使用HTTP方式加載,瀏覽器應該自動把全部嘗試使用HTTP的請求自動替換爲HTTPS請求。
  3. X-Frame-Options:DENY 點擊劫持(ClickJacking)是一種視覺上的欺騙手段。攻擊者使用一個透明的iframe,覆蓋在一個網頁上,而後誘使用戶在網頁上進行操做,此時用戶將在不知情的狀況下點擊透明的iframe頁面。經過調整iframe頁面的位置,能夠誘使用戶剛好點擊在iframe頁面的一些功能性按鈕上。
    HTTP響應頭信息中的X-Frame-Options,能夠指示瀏覽器是否應該加載一個iframe中的頁面。若是服務器響應頭信息中沒有X-Frame-Options,則該網站存在ClickJacking攻擊風險。網站能夠經過設置X-Frame-Options阻止站點內的頁面被其餘頁面嵌入從而防止點擊劫持。
    解決方案:
    修改web服務器配置,添加X-Frame-Options響應頭。賦值有以下三種:
    一、DENY:不能被嵌入到任何iframe或者frame中。
    二、SAMEORIGIN:頁面只能被本站頁面嵌入到iframe或者frame中
    三、ALLOW-FROM uri:只能被嵌入到指定域名的框架中
  4. X-Content-Type-Options:nosniff

    若是服務器發送響應頭 "X-Content-Type-Options: nosniff",則 script 和 styleSheet 元素會拒絕包含錯誤的 MIME 類型的響應。這是一種安全功能,有助於防止基於 MIME 類型混淆的攻擊。

     

    簡單理解爲:經過設置"X-Content-Type-Options: nosniff"響應標頭,對 script 和 styleSheet 在執行是經過MIME 類型來過濾掉不安全的文件

    服務器發送含有 "X-Content-Type-Options: nosniff" 標頭的響應時,此更改會影響瀏覽器的行爲。

  5. Referrer-Policy:no-referrer

    referrer是HTTP請求header的報文頭,用於指明當前流量的來源參考頁面。經過這個信息,咱們能夠知道訪客是怎麼來到當前頁面的。這對於Web Analytics很是重要,能夠用於分析不一樣渠道流量分佈、用戶搜索的關鍵詞等。
    可是,這個字段同時會形成用戶敏感信息泄漏(如:帶有敏感信息的重置密碼URL,若被Web Analytics收集,則存在密碼被重置的危險)。

    Referrer Policy States

    新的Referrer規定了五種策略:

  • No Referrer:任何狀況下都不發送Referrer信息
  • No Referrer When Downgrade:僅當協議降級(如HTTPS頁面引入HTTP資源)時不發送Referrer信息。是大部分瀏覽器默認策略。
  • Origin Only:發送只包含host部分的referrer.
  • Origin When Cross-origin:僅在發生跨域訪問時發送只包含host的Referer,同域下仍是完整的。與Origin Only的區別是多判斷了是否Cross-origin。協議、域名和端口都一致,瀏覽器才認爲是同域。
  • Unsafe URL:所有都發送Referrer信息。最寬鬆最不安全的策略。
  1. 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' 內容安全策略(CSP),其核心思想十分簡單:網站經過發送一個 CSP 頭部,來告訴瀏覽器什麼是被受權執行的與什麼是須要被禁止的。其被譽爲專門爲解決XSS攻擊而生的神器。具體參考http://www.javashuo.com/article/p-rmqcvuwn-ng.html
  2. X-Download-Options:noopen

    用於放置直接打開用戶下載文件。

    X-Download-Options: noopen
  • noopen 用於指定IE 8以上版本的用戶不打開文件而直接保存文件。在下載對話框中不顯示「打開」選項。
  1. X-Permitted-Cross-Domain-Policies:none

    用於指定當不能將"crossdomain.xml"文件(當須要從別的域名中的某個文件中讀取 Flash 內容時用於進行必要設置的策略文件)放置在網站根目錄等場合時採起的替代策略。

    X-Permitted-Cross-Domain-Policies: master-only
  • master-only 只容許使用主策略文件(/crossdomain.xml)

若是你想修改這些Header的值,可以使用以下配置:

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
      filter:
        secure-headers:
          xss-protection-header: 1;mode=block

上面的header對應的後綴:

  1. xss-protection-header
  2. strict-transport-security
  3. frame-options
  4. content-type-options
  5. referrer-policy
  6. content-security-policy
  7. download-options
  8. permitted-cross-domain-policies

若是想禁用某些Header,可以使用以下配置:spring.cloud.gateway.filter.secure-headers.disable ,多個用 , 分隔。例如:

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
      filter:
        secure-headers:
          disable: frame-options,download-options
  • SetPath
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/{segment}
        filters:
        #使用/{segment}來代替/user-center/{segment}後轉發
        - SetPath=/{segment}

用意跟以前差很少。

  • SetResponseHeader

在User項目中有這樣一個Controller方法

@GetMapping("/addhead")
public String addHeader(HttpServletRequest request, HttpServletResponse response) {
    response.addHeader("X-Response-Foo","Foo");
    return "header";
}
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #將響應頭X-Response-Foo的值更改成dee
        - SetResponseHeader=X-Response-Foo,dee

  • SetStatus

修改響應的狀態碼,值能夠是數字,也能夠是字符串。但必定要是Spring HttpStatus 枚舉類中的值。

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #修改返回狀態碼爲401
        - SetStatus=401

這裏是能夠正常返回結果的,只不過狀態碼被修改成401

  • StripPrefix

數字表示要截斷的路徑的數量。

  • Retry

針對不一樣的響應作重試,可配置以下參數:

  1. retries: 重試次數
  2. statuses: 須要重試的狀態碼,取值在 org.springframework.http.HttpStatus 中
  3. methods: 須要重試的請求方法,取值在 org.springframework.http.HttpMethod 中
  4. series: HTTP狀態碼系列,取值在 org.springframework.http.HttpStatus.Series 中
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #若是方法未找到,重試3次
        - name: Retry
          args:
            retries: 3
            statuses: NOT_FOUND
  • RequestSize

爲後端服務設置收到的最大請求包大小。若是請求大小超過設置的值,則返回 413 Payload Too Large 。默認值是5M
不過這裏我設置了1字節,好像不起做用。

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #若是請求包超過1字節,返回413
        - name: RequestSize
          args:
            maxSize: 1

經測試無效

  • Modify Request Body

可用於在Gateway將請求發送給後端微服務以前,修改請求體內容。該過濾器工廠目前處於BETA狀態,不建議使用。

  • Modify Response Body

可用於修改響應體內容。該過濾器工廠目前處於BETA狀態,不建議使用。

  • Default

若是你想爲全部路由添加過濾器,可以使用該屬性。

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
      #該配置對全部的routes.id均有效
      default-filters:
      #跳轉後省略第一個通配
      - StripPrefix=1

自定義過濾器工廠

  • 過濾器生命週期
  1. pre: Gateway轉發請求以前
  2. post: Gateway轉發請求以後
  • 自定義過濾器工廠的方式
  1. 繼承: AbstractGatewayFilterFactory
  2. 繼承: AbstractNameValueGatewayFilterFactory
  • 核心API
  1. exchange.getRequest().mutate().xxx //修改request
  2. exchange.mutate().xxx //修改exchange
  3. chain.filter(exchange) //傳遞給下一個過濾器處理
  4. exchange.getResponse //拿到響應
  • 編寫一個過濾器工廠

如今咱們來寫一個打印日誌的過濾器工廠,該自定義過濾器工廠必須以GatewayFilterFactory結尾

@Slf4j
@Component
public class PreLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (((exchange, chain) -> {
            log.info("請求進來了...{},{}",config.getName(),config.getValue());
            //獲取請求
            ServerHttpRequest modifiedRequest = exchange.getRequest()
                    .mutate()
                    .build();
            //獲取exchange
            ServerWebExchange modifiedExchange = exchange.mutate()
                    .request(modifiedRequest)
                    .build();
            //傳遞給下一個過濾器
            return chain.filter(modifiedExchange);
        }));
    }
}

配置文件

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
      - id: gate
        uri: lb://user
        predicates:
        #由/user-center來匹配跳轉
        - Path=/user-center/**
        filters:
        #跳轉後省略第一個通配
        - StripPrefix=1
        #打印a,b日誌
        - PreLog=a,b

運行後,咱們經過網關訪問API後,打印日誌以下

2019-12-20 14:09:48.066  INFO 2702 --- [ctor-http-nio-2] c.c.c.m.g.c.PreLogGatewayFilterFactory   : 請求進來了...a,b

全局過濾器

1 Combined Global Filter and GatewayFilter Ordering
2 Forward Routing Filter
3 LoadBalancerClient Filter
4 Netty Routing Filter
5 Netty Write Response Filter
6 RouteToRequestUrl Filter
7 Websocket Routing Filter
8 Gateway Metrics Filter
9 Marking An Exchange As Routed

  • Global Filter and GatewayFilter Ordering

當請求到來時,Filtering Web Handler 處理器會添加全部 GlobalFilter 實例和匹配的 GatewayFilter 實例到過濾器鏈中。

過濾器鏈會使用 org.springframework.core.Ordered 註解所指定的順序,進行排序。Spring Cloud Gateway區分了過濾器邏輯執行的」pre」和」post」階段,因此優先級高的過濾器將會在pre階段最早執行,優先級最低的過濾器則在post階段最後執行。數值越小越靠前執行

@Slf4j
@Configuration
public class GlobleFilters {
    @Bean
    @Order(-1)
    public GlobalFilter a() {
        return ((exchange, chain) -> {
            log.info("first pre filter");
            return chain.filter(exchange).then(Mono.fromRunnable(
                    () -> log.info("third post filter")));
        });
    }

    @Bean
    @Order(0)
    public GlobalFilter b() {
        return ((exchange, chain) -> {
            log.info("second pre filter");
            return chain.filter(exchange).then(Mono.fromRunnable(
                    () -> log.info("second post filter")));
        });
    }

    @Bean
    @Order(1)
    public GlobalFilter c() {
        return ((exchange, chain) -> {
            log.info("third pre filter");
            return chain.filter(exchange).then(Mono.fromRunnable(
                    () -> log.info("first post filter")));
        });
    }
}

當有網關轉發請求時

2019-12-20 15:03:34.263  INFO 3380 --- [ctor-http-nio-2] c.c.c.m.gateway.config.GlobleFilters     : first pre filter
2019-12-20 15:03:34.263  INFO 3380 --- [ctor-http-nio-2] c.c.c.m.gateway.config.GlobleFilters     : second pre filter
2019-12-20 15:03:34.263  INFO 3380 --- [ctor-http-nio-2] c.c.c.m.gateway.config.GlobleFilters     : third pre filter
2019-12-20 15:03:34.302  INFO 3380 --- [ctor-http-nio-7] c.c.c.m.gateway.config.GlobleFilters     : first post filter
2019-12-20 15:03:34.302  INFO 3380 --- [ctor-http-nio-7] c.c.c.m.gateway.config.GlobleFilters     : second post filter
2019-12-20 15:03:34.302  INFO 3380 --- [ctor-http-nio-7] c.c.c.m.gateway.config.GlobleFilters     : third post filter

  • Forward Routing Filter

整合Sentinel限流

Sentinel的版本必須在1.6及以上,咱們這裏爲1.7

pom

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

添加配置類

@Configuration
public class GatewayConfig {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfig(ObjectProvider<List<ViewResolver>> viewResolverProvider,
                         ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolverProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initGatewayRules();
    }

    /**
     * 配置限流規則
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("gate")
                    .setCount(1) //限流閾值
                    .setIntervalSec(1)); //統計時間窗口,單位是秒,默認是1秒
        GatewayRuleManager.loadRules(rules);
    }
}

咱們這裏設置的爲1秒鐘只能經過一個請求。

若是咱們在1秒鐘內請求兩次或以上,就會產生限流提示。

相關文章
相關標籤/搜索