一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Cloud Gateway 默認的filter功能和執行順序介紹

Spring Cloud Gateway 默認的filter功能和執行順序介紹

2022-02-20 11:39giafei Java教程

這篇文章主要介紹了Spring Cloud Gateway 默認的filter功能和執行順序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Spring Cloud Gateway 默認的filter功能和執行順序

有效性

Spring Cloud Gateway 2.0.0.RELEASE

調試方法

新建一個GlobalFilter,在filter中加斷點即可調試filter,通過chain參數可以查看其它的filter及執行順序(order)

filters(按執行順序)

1. AdaptCachedBodyGlobalFilter

核心代碼

public int getOrder() {
  return Ordered.HIGHEST_PRECEDENCE + 1000;
}
public static final String CACHED_REQUEST_BODY_KEY = "cachedRequestBody";
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  Flux<DataBuffer> body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_KEY, null);
  if (body != null) {
      ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
          @Override
          public Flux<DataBuffer> getBody() {
              return body;
          }
      };
      return chain.filter(exchange.mutate().request(decorator).build());
  }
  return chain.filter(exchange);
}

提供替換request 的 body的能力

2.NettyWriteResponseFilter

核心代碼

public static final int WRITE_RESPONSE_FILTER_ORDER = -1;
public int getOrder() {
  return WRITE_RESPONSE_FILTER_ORDER;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  return chain.filter(exchange).then(Mono.defer(() -> {
      //見 后文的 NettyRoutingFilter
      HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);
      ServerHttpResponse response = exchange.getResponse();
      NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
      
      final Flux<NettyDataBuffer> body = clientResponse.receive()
              .map(factory::wrap);
      MediaType contentType = response.getHeaders().getContentType();
      return (isStreamingMediaType(contentType) ?
              response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body));
  }));
}

具體的將被代理的服務的內容返回的類,文檔

3.ForwardPathFilter

核心代碼

public int getOrder() {
  return 0;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
  URI routeUri = route.getUri();
  String scheme = routeUri.getScheme();
  if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
      return chain.filter(exchange);
  }
  exchange = exchange.mutate().request(
          exchange.getRequest().mutate().path(routeUri.getPath()).build())
          .build();
  return chain.filter(exchange);
}

forward協議的url替換類

4.在Route中配置的各種GatewayFilter

核心代碼

/**
* RouteDefinitionRouteLocator#loadGatewayFilters GatewayFilter的order
*/
ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
for (int i = 0; i < filters.size(); i++) {
  GatewayFilter gatewayFilter = filters.get(i);
  if (gatewayFilter instanceof Ordered) {
      ordered.add(gatewayFilter);
  }
  else {
      ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
  }
}
return ordered;

根據配置不同實現具體的功能,詳見文檔

5.RouteToRequestUrlFilter

核心代碼

public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;
public int getOrder() {
  return ROUTE_TO_URL_FILTER_ORDER;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
  if (route == null) {
      return chain.filter(exchange);
  }
  URI uri = exchange.getRequest().getURI();
  boolean encoded = containsEncodedParts(uri);
  URI routeUri = route.getUri();
  
  //匹配 http:http://locahost:80/a/b/c?q=1,并把第一個 http: 去掉
  if (hasAnotherScheme(routeUri)) {
      // uri格式 [scheme:]scheme-specific-part[#fragment]
      exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());
      routeUri = URI.create(routeUri.getSchemeSpecificPart());
  }
  URI requestUrl = UriComponentsBuilder.fromUri(uri)
          .uri(routeUri)
          .build(encoded)
          .toUri();
  exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
  return chain.filter(exchange);
}
private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*";
static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX);
static boolean hasAnotherScheme(URI uri) {
  return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null
          && uri.getRawPath() == null;
}

路由功能的具體執行類,文檔

6.LoadBalancerClientFilter(如果啟用了eureka)

核心代碼

public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
public int getOrder() {
  return LOAD_BALANCER_CLIENT_FILTER_ORDER;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
  String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
  if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
      return chain.filter(exchange);
  }
  //一大波轉換操作
  addOriginalRequestUrl(exchange, url);
  final ServiceInstance instance = loadBalancer.choose(url.getHost());
  if (instance == null) {
      throw new NotFoundException("Unable to find instance for " + url.getHost());
  }
  URI uri = exchange.getRequest().getURI();
  String overrideScheme = null;
  if (schemePrefix != null) {
      overrideScheme = url.getScheme();
  }
  URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
  
  //轉換后的url填入 GATEWAY_REQUEST_URL_ATTR 屬性
  exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
  return chain.filter(exchange);
}

lb協議的路由功能,文檔

7.WebsocketRoutingFilter

核心代碼

public int getOrder() {
  return Ordered.LOWEST_PRECEDENCE - 1;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  //upgrade頭 見https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
  //或見 https://httpwg.org/specs/rfc7230.html#header.upgrade
  changeSchemeIfIsWebSocketUpgrade(exchange);
  //跳過一大波參數檢查與參數獲取
  return this.webSocketService.handleRequest(exchange,
          new ProxyWebSocketHandler(requestUrl, this.webSocketClient,
                  filtered, protocols));
}
/**
* ProxyWebSocketHandler#handle 橋接兩個webSocket
*/
public Mono<Void> handle(WebSocketSession session) { //session為客戶端
  return client.execute(url, this.headers, new WebSocketHandler() {
      @Override
      public Mono<Void> handle(WebSocketSession proxySession) {   //proxySession為被代理的WebSocket
          Mono<Void> proxySessionSend = proxySession
                  .send(session.receive().doOnNext(WebSocketMessage::retain));
          Mono<Void> serverSessionSend = session
                  .send(proxySession.receive().doOnNext(WebSocketMessage::retain));
          return Mono.zip(proxySessionSend, serverSessionSend).then();
      }
      
      //省略其它方法
  });
} 

WebSocket的代理功能,文檔

8.NettyRoutingFilter

核心代碼

public int getOrder() {
  return Ordered.LOWEST_PRECEDENCE;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
      //省略一大波參數獲取和參數校驗
      final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());
      final String url = requestUrl.toString();
      return this.httpClient.request(method, url, req -> {
          //省略http數據發送代碼
      }).doOnNext(res -> {
          ServerHttpResponse response = exchange.getResponse();
          
          HttpHeaders headers = new HttpHeaders();
          res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
          //注意,如果ContentType為null會 NPE,特別是301或302跳轉
          exchange.getAttributes().put("original_response_content_type", headers.getContentType());
          //省略其它http解析代碼
          exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);    //與前面的 NettyWriteResponseFilter 對應
      }).then(chain.filter(exchange));
  }
} 

http協議的代理功能,文檔

9.ForwardRoutingFilter

核心代碼

public int getOrder() {
  return Ordered.LOWEST_PRECEDENCE;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
  String scheme = requestUrl.getScheme();
  if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
      return chain.filter(exchange);
  }
  setAlreadyRouted(exchange);
  if (log.isTraceEnabled()) {
      log.trace("Forwarding to URI: "+requestUrl);
  }
  return this.dispatcherHandler.handle(exchange);
}

將未處理的forward協議的請求交由spring來處理,文檔

其中 NettyRoutingFilter 和 NettyWriteResponseFilter 內置有 WebClientHttpRoutingFilter和WebClientWriteResponseFilter 作為備用替換版本。

 

spring cloud gateway之filter實戰

1、filter的作用和生命周期

由filter工作流程點,可以知道filter有著非常重要的作用,在“pre”類型的過濾器可以做參數校驗、權限校驗、流量監控、日志輸出、協議轉換等,在“post”類型的過濾器中可以做響應內容、響應頭的修改,日志的輸出,流量監控等。首先需要弄清一點為什么需要網關這一層,這就不得不說下filter的作用了。

作用

當我們有很多個服務時,比如下圖中的user-service、goods-service、sales-service等服務,客戶端請求各個服務的Api時,每個服務都需要做相同的事情,比如鑒權、限流、日志輸出等。

Spring Cloud Gateway 默認的filter功能和執行順序介紹

對于這樣重復的工作,有沒有辦法做的更好,答案是肯定的。在微服務的上一層加一個全局的權限控制、限流、日志輸出的Api Gatewat服務,然后再將請求轉發到具體的業務服務層。這個Api Gateway服務就是起到一個服務邊界的作用,外接的請求訪問系統,必須先通過網關層。

生命周期

Spring Cloud Gateway同zuul類似,有“pre”和“post”兩種方式的filter。客戶端的請求先經過“pre”類型的filter,然后將請求轉發到具體的業務服務,比如上圖中的user-service,收到業務服務的響應之后,再經過“post”類型的filter處理,最后返回響應到客戶端。

Spring Cloud Gateway 默認的filter功能和執行順序介紹

與zuul不同的是,filter除了分為“pre”和“post”兩種方式的filter外,在Spring Cloud Gateway中,filter從作用范圍可分為另外兩種,一種是針對于單個路由的gateway filter,它在配置文件中的寫法同predict類似;另外一種是針對于所有路由的global gateway filer。現在從作用范圍劃分的維度來講解這兩種filter。

gateway filter

過濾器允許以某種方式修改傳入的HTTP請求或傳出的HTTP響應。過濾器可以限定作用在某些特定請求路徑上。 Spring Cloud Gateway包含許多內置的GatewayFilter工廠。

GatewayFilter工廠同上一篇介紹的Predicate工廠類似,都是在配置文件application.yml中配置,遵循了約定大于配置的思想,只需要在配置文件配置GatewayFilter Factory的名稱,而不需要寫全部的類名,比如AddRequestHeaderGatewayFilterFactory只需要在配置文件中寫AddRequestHeader,而不是全部類名。在配置文件中配置的GatewayFilter Factory最終都會相應的過濾器工廠類處理。

Spring Cloud Gateway 內置的過濾器工廠一覽表如下:

Spring Cloud Gateway 默認的filter功能和執行順序介紹

現在挑幾個常見的過濾器工廠來講解,每一個過濾器工廠在官方文檔都給出了詳細的使用案例,如果不清楚的還可以在org.springframework.cloud.gateway.filter.factory看每一個過濾器工廠的源碼。

2、AddRequestHeader GatewayFilter Factory

A.創建子工程gateway-filter

B.引入相關的依賴

<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>
	</dependencies>

C.application.yml

server:
port: 8081
spring:
profiles:
  active: add_request_header_route
---
spring:
cloud:
  gateway:
    routes:
    - id: add_request_header_route
      uri: http://httpbin.org:80/get
      filters:
      - AddRequestHeader=X-Request-Foo, Bar
      predicates:
      - After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: add_request_header_route

在上述的配置中,工程的啟動端口為8081,配置文件為add_request_header_route,在add_request_header_route配置中,配置了roter的id為add_request_header_route,路由地址為http://httpbin.org:80/get,該router有AfterPredictFactory,有一個filter為AddRequestHeaderGatewayFilterFactory(約定寫成AddRequestHeader),AddRequestHeader過濾器工廠會在請求頭加上一對請求頭,名稱為X-Request-Foo,值為Bar。為了驗證AddRequestHeaderGatewayFilterFactory是怎么樣工作的,查看它的源碼,AddRequestHeaderGatewayFilterFactory的源碼如下:

public class AddRequestHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
	@Override
	public GatewayFilter apply(NameValueConfig config) {
		return (exchange, chain) -> {
			ServerHttpRequest request = exchange.getRequest().mutate()
					.header(config.getName(), config.getValue())
					.build();
			return chain.filter(exchange.mutate().request(request).build());
		};
  }
}

由上面的代碼可知,根據舊的ServerHttpRequest創建新的 ServerHttpRequest ,在新的ServerHttpRequest加了一個請求頭,然后創建新的 ServerWebExchange ,提交過濾器鏈繼續過濾。

啟動工程,通過curl命令來模擬請求:

curl localhost:8081

最終顯示了從 http://httpbin.org:80/get得到了請求,響應如下:

{
"args": {},
"headers": {
  "Accept": "*/*",
  "Connection": "close",
  "Forwarded": "proto=http;host=\"localhost:8081\";for=\"0:0:0:0:0:0:0:1:56248\"",
  "Host": "httpbin.org",
  "User-Agent": "curl/7.58.0",
  "X-Forwarded-Host": "localhost:8081",
  "X-Request-Foo": "Bar"
},
"origin": "0:0:0:0:0:0:0:1, 210.22.21.66",
"url": "http://localhost:8081/get"
}

可以上面的響應可知,確實在請求頭中加入了X-Request-Foo這樣的一個請求頭,在配置文件中配置的AddRequestHeader過濾器工廠生效。

跟AddRequestHeader過濾器工廠類似的還有AddResponseHeader過濾器工廠,在此就不再重復。

RewritePath GatewayFilter Factory

在Nginx服務啟中有一個非常強大的功能就是重寫路徑,Spring Cloud Gateway默認也提供了這樣的功能,這個功能是Zuul沒有的。在配置文件中加上以下的配置:

spring:
profiles:
  active: rewritepath_route
---
spring:
cloud:
  gateway:
    routes:
    - id: rewritepath_route
      uri: https://blog.csdn.net
      predicates:
      - Path=/foo/**
      filters:
      - RewritePath=/foo/(?<segment>.*), /$\{segment}
profiles: rewritepath_route

上面的配置中,所有的/foo/**開始的路徑都會命中配置的router,并執行過濾器的邏輯,在本案例中配置了RewritePath過濾器工廠,此工廠將/foo/(?.*)重寫為{segment},然后轉發到https://blog.csdn.net。比如在網頁上請求localhost:8081/foo/forezp,此時會將請求轉發到https://blog.csdn.net/forezp的頁面,比如在網頁上請求localhost:8081/foo/forezp/1,頁面顯示404,就是因為不存在https://blog.csdn.net/forezp/1這個頁面。

自定義過濾器

Spring Cloud Gateway內置了19種強大的過濾器工廠,能夠滿足很多場景的需求,那么能不能自定義自己的過濾器呢,當然是可以的。在spring Cloud Gateway中,過濾器需要實現GatewayFilter和Ordered2個接口。寫一個RequestTimeFilter,代碼如下:

public class RequestTimeFilter implements GatewayFilter, Ordered {
  private static final Log log = LogFactory.getLog(GatewayFilter.class);
  private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
      return chain.filter(exchange).then(
              Mono.fromRunnable(() -> {
                  Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                  if (startTime != null) {
                      log.info(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
                  }
              })
      );
  }
  @Override
  public int getOrder() {
      return 0;
  }
}

在上面的代碼中,Ordered中的int getOrder()方法是來給過濾器設定優先級別的,值越大則優先級越低。還有有一個filterI(exchange,chain)方法,在該方法中,先記錄了請求的開始時間,并保存在ServerWebExchange中,此處是一個“pre”類型的過濾器,然后再chain.filter的內部類中的run()方法中相當于"post"過濾器,在此處打印了請求所消耗的時間。然后將該過濾器注冊到router中,代碼如下:

@Bean
  public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
      // @formatter:off
      return builder.routes()
              .route(r -> r.path("/customer/**")
                      .filters(f -> f.filter(new RequestTimeFilter())
                              .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                      .uri("http://httpbin.org:80/get")
                      .order(0)
                      .id("customer_filter_router")
              )
              .build();
      // @formatter:on
  }

自定義過濾器工廠

在上面的自定義過濾器中,有沒有辦法自定義過濾器工廠類呢?這樣就可以在配置文件中配置過濾器了。現在需要實現一個過濾器工廠,在打印時間的時候,可以設置參數來決定是否打印請參數。查看GatewayFilterFactory的源碼,可以發現GatewayFilterfactory的層級如下:

Spring Cloud Gateway 默認的filter功能和執行順序介紹

過濾器工廠的頂級接口是GatewayFilterFactory,我們可以直接繼承它的兩個抽象類來簡化開發AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,這兩個抽象類的區別就是前者接收一個參數(像StripPrefix和我們創建的這種),后者接收兩個參數(像AddResponseHeader)。

過濾器工廠的頂級接口是GatewayFilterFactory,有2個兩個較接近具體實現的抽象類,分別為AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,這2個類前者接收一個參數,比如它的實現類RedirectToGatewayFilterFactory;后者接收2個參數,比如它的實現類AddRequestHeaderGatewayFilterFactory類。現在需要將請求的日志打印出來,需要使用一個參數,這時可以參照RedirectToGatewayFilterFactory的寫法。

public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {
  private static final Log log = LogFactory.getLog(GatewayFilter.class);
  private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
  private static final String KEY = "withParams";
  @Override
  public List<String> shortcutFieldOrder() {
      return Arrays.asList(KEY);
  }
  public RequestTimeGatewayFilterFactory() {
      super(Config.class);
  }
  @Override
  public GatewayFilter apply(Config config) {
      return (exchange, chain) -> {
          exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
          return chain.filter(exchange).then(
                  Mono.fromRunnable(() -> {
                      Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                      if (startTime != null) {
                          StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
                                  .append(": ")
                                  .append(System.currentTimeMillis() - startTime)
                                  .append("ms");
                          if (config.isWithParams()) {
                              sb.append(" params:").append(exchange.getRequest().getQueryParams());
                          }
                          log.info(sb.toString());
                      }
                  })
          );
      };
  }
  public static class Config {
      private boolean withParams;
      public boolean isWithParams() {
          return withParams;
      }
      public void setWithParams(boolean withParams) {
          this.withParams = withParams;
      }
  }
}

在上面的代碼中 apply(Config config)方法內創建了一個GatewayFilter的匿名類,具體的實現邏輯跟之前一樣,只不過加了是否打印請求參數的邏輯,而這個邏輯的開關是config.isWithParams()。靜態內部類類Config就是為了接收那個boolean類型的參數服務的,里邊的變量名可以隨意寫,但是要重寫List shortcutFieldOrder()這個方法。。

需要注意的是,在類的構造器中一定要調用下父類的構造器把Config類型傳過去,否則會報ClassCastException

最后,需要在工程的啟動文件Application類中,向Srping Ioc容器注冊RequestTimeGatewayFilterFactory類的Bean。

@Bean
  public RequestTimeGatewayFilterFactory elapsedGatewayFilterFactory() {
      return new RequestTimeGatewayFilterFactory();
  }

然后可以在配置文件中配置如下:

spring:
profiles:
  active: elapse_route
---
spring:
cloud:
  gateway:
    routes:
    - id: elapse_route
      uri: http://httpbin.org:80/get
      filters:
      - RequestTime=false
      predicates:
      - After=2017-01-20T17:42:47.789-07:00[America/Denver]
profiles: elapse_route

啟動工程,在瀏覽器上訪問localhost:8081?name=forezp,可以在控制臺上看到,日志輸出了請求消耗的時間和請求參數。

global filter

Spring Cloud Gateway根據作用范圍劃分為GatewayFilter和GlobalFilter,二者區別如下:

  • GatewayFilter : 需要通過spring.cloud.routes.filters 配置在具體路由下,只作用在當前路由上或通過spring.cloud.default-filters配置在全局,作用在所有路由上
  • GlobalFilter : 全局過濾器,不需要在配置文件中配置,作用在所有的路由上,最終通過GatewayFilterAdapter包裝成GatewayFilterChain可識別的過濾器,它為請求業務以及路由的URI轉換為真實業務服務的請求地址的核心過濾器,不需要配置,系統初始化時加載,并作用在每個路由上。

Spring Cloud Gateway框架內置的GlobalFilter如下:

Spring Cloud Gateway 默認的filter功能和執行順序介紹

上圖中每一個GlobalFilter都作用在每一個router上,能夠滿足大多數的需求。但是如果遇到業務上的定制,可能需要編寫滿足自己需求的GlobalFilter。在下面的案例中將講述如何編寫自己GlobalFilter,該GlobalFilter會校驗請求中是否包含了請求參數“token”,如何不包含請求參數“token”則不轉發路由,否則執行正常的邏輯。代碼如下:

public class TokenFilter implements GlobalFilter, Ordered {
  Logger logger=LoggerFactory.getLogger( TokenFilter.class );
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      String token = exchange.getRequest().getQueryParams().getFirst("token");
      if (token == null || token.isEmpty()) {
          logger.info( "token is empty..." );
          exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
          return exchange.getResponse().setComplete();
      }
      return chain.filter(exchange);
  }
  @Override
  public int getOrder() {
      return -100;
  }
}

在上面的TokenFilter需要實現GlobalFilter和Ordered接口,這和實現GatewayFilter很類似。然后根據ServerWebExchange獲取ServerHttpRequest,然后根據ServerHttpRequest中是否含有參數token,如果沒有則完成請求,終止轉發,否則執行正常的邏輯。

然后需要將TokenFilter在工程的啟動類中注入到Spring Ioc容器中,代碼如下:

@Bean
public TokenFilter tokenFilter(){
      return new TokenFilter();
}

啟動工程,使用curl命令請求:

curl localhost:8081/customer/123

可以看到請沒有被轉發,請求被終止,并在控制臺打印了如下日志:

2018-11-16 15:30:13.543 INFO 19372 ― [ctor-http-nio-2] gateway.TokenFilter

上面的日志顯示了請求進入了沒有傳“token”的邏輯。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://www.jianshu.com/p/8fa0f75e83bc

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品久久免费观看 | 果冻传媒在线播放观看w | 99这里精品| 天天做日日爱 | 国产一卡 | 美女草b| 经典三级四虎在线观看 | 99热碰| 欧美成a人片免费看久久 | 牛牛影院成人免费网页 | 羞羞视频麻豆 | 免费国产在线视频 | 日本www午夜色在线视频 | 国产肥女bbwbbw | 日韩在线视精品在亚洲 | 欧美ggg666 | 俄罗斯性高清完整版 | 免费一级欧美片在线观免看 | 日本伊人色综合网 | 日出水了特别黄的视频 | www久久久| 欧美成人禁片在线观看俄罗斯 | 国产一区二区三区久久小说 | 婷婷综合七月激情啪啪 | 3d动漫美女物被遭强视频 | 天堂伊人 | 欧美性野久久久久久久久 | 国产精品福利短视在线播放频 | 国产伦码精品一区二区三区 | 国产精品亚洲一区二区久久 | 亚洲欧美国产另类 | www.东方影库 | zoomkool最新版 | 精品国产成人a区在线观看 精品国产91久久久久久久 | 日本伊人色 | 青青色在线观看 | 欧美日韩国产超高清免费看片 | 免费看一级毛片 | 日本成人黄色网址 | 亚洲国产第一区二区香蕉日日 | 色哟哟哟|