Spring Cloud Gateway 默认的filter功能和执行顺序介绍

作者:giafei 时间:2021-07-01 21:31:46 

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

标签:SpringCloud,Gateway,filter,执行顺序
0
投稿

猜你喜欢

  • C#实现文本文件读写方法汇总

    2023-10-27 16:57:40
  • Java开发岗位面试被问到反射怎么办

    2023-01-07 00:09:12
  • C#实现日期格式转换的公共方法类实例

    2023-05-16 10:06:54
  • Java JDBC导致的反序列化攻击原理解析

    2023-09-24 15:38:42
  • Spring Security实现两周内自动登录"记住我"功能

    2021-12-15 17:59:22
  • Java Socket实现传输压缩对象的方法示例

    2022-12-18 06:52:30
  • springmvc前台向后台传值几种方式总结(从简单到复杂)

    2023-04-13 22:20:50
  • Spring整合Junit的使用详解

    2022-11-20 18:33:17
  • Java值传递之swap()方法不能交换的解决

    2023-11-12 20:54:50
  • 使用java实现猜拳小游戏

    2023-11-25 17:30:16
  • 推荐两款java开发实用工具 hutool 和 lombok

    2022-04-06 10:20:14
  • java8 stream自定义分组求和并排序的实现

    2022-09-12 04:08:26
  • Spring MVC过滤器-登录过滤的代码实现

    2021-06-05 15:01:43
  • 基于C语言string函数的详解

    2023-06-28 05:33:25
  • Springboot+SpringSecurity+JWT实现用户登录和权限认证示例

    2021-11-14 11:06:11
  • 浅谈spring security入门

    2023-02-20 04:48:58
  • Java代理模式与动态代理之间的关系以及概念

    2022-03-15 18:05:34
  • Java 实现网络爬虫框架详细代码

    2021-12-11 05:15:43
  • C#编程实现获取文件夹中所有文件的文件名

    2022-11-16 07:53:10
  • Java如何基于ProcessBuilder类调用外部程序

    2023-11-27 20:19:57
  • asp之家 软件编程 m.aspxhome.com