Abstract: This paper mainly introduces what is GatewayFilter and GlobalFilter, as well as the differences and connections. Then it introduces how to customize the use of GatewayFilter and GlobalFilter in Spring Cloud Gateway.
1. Filter of spring cloud gateway
The Filter in Spring Cloud gateway can be divided into two types in terms of interface implementation: GatewayFilter and GlobalFilter.
1.1 difference between gateway filter and GlobalFilter
At a high level, Global filters are applied to all routes, while a Gateway filter will be applied to an individual route (s). This will be explained in the following case.
1.2 code address of this document
https://github.com/SoftwareKing/sc-gateway/tree/master/ch2
2. GatewayFilter and GlobalFilter
2.1 GatewayFilter
2.1.1 what is GatewayFilter
Contract for interception-style, chained processing of Web requests that may be used to implement cross-cutting, application-agnostic requirements such as security, timeouts, and others. Specific to a gateway copied from WebFilter > gateway Filter is copied from WebFilter, which is equivalent to a Filter. It can Filter and crosscut the accessed URL. Application scenarios such as timeout, security, etc.
From the source code of Spring Cloud Gateway, as shown below, we can see the usage scenario of GatewayFilter:
/** * Contract for interception-style, chained processing of Web requests that may * be used to implement cross-cutting, application-agnostic requirements such * as security, timeouts, and others. Specific to a Gateway * * Copied from WebFilter * * @author Rossen Stoyanchev * @since 5.0 */ public interface GatewayFilter extends ShortcutConfigurable { String NAME_KEY = "name"; String VALUE_KEY = "value"; /** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
The methods defined in the GatewayFilter and GlobalFilter interfaces are the same as Mono filter(ServerWebExchange exchange, GatewayFilterChain chain). The only difference is that GatewayFilter inherits ShortcutConfigurable and GlobalFilter does not inherit anything.
2.1.2 custom gatewayfilter
Such as org xujin. sc.filter. As shown in the customfilter code, the processing time of route forwarding is counted through the custom GatewayFilter.
package org.xujin.sc.filter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * Count the processing time of a route or a route * @author xujin */ public class CustomFilter implements GatewayFilter, Ordered { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String COUNT_Start_TIME = "countStartTime"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(COUNT_Start_TIME, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(COUNT_Start_TIME); Long endTime=(System.currentTimeMillis() - startTime); if (startTime != null) { log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms"); } }) ); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
2.1.3 Gateway Filter is bound with RouteLocator
At org xujin. The customerRouteLocator in sc.gatewayserverapplication is as follows:
@SpringBootApplication public class GatewayServerApplication { @Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(r -> r.path("/test/prefix/**") .filters(f -> f.stripPrefix(2) .filter(new CustomFilter()) .addResponseHeader("X-Response-test", "test")) .uri("lb://SC-CONSUMER") .order(0) .id("test_consumer_service") ) .build(); } public static void main(String[] args) { SpringApplication.run(GatewayServerApplication.class, args); } }
-
r.path("/ test/prefix / * *") indicates that the access prefix is customized. When routing and forwarding through the real Gateway, f.stripPrefix(2) will be used to remove the prefix. > usage scenario: externally exposed URL s can be marked by prefixing groups.
-
Filter (New customfilter() > filter (New customfilter()) means that the custom filter is added to the filter chain for execution. Note that the custom GlobalFilter does not need to be added.
-
uri("lb://SC-CONSUMER") > uri("lb://SC-CONSUMER") indicates Spring Cloud Gateway's support for spring application. Name equals to the URL in the SC consumer source service application for protocol adaptation forwarding.
2.1.4 the tests are as follows:
1. Visit http://localhost:9000/SC-CONSUMER/hello/xujin Normal access.
DEBUG 31658 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: CompositeDiscoveryClient_SC-CONSUMER 2018-05-27 09:58:07.905 DEBUG 31658 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:9000/SC-CONSUMER/hello/xujin] to Route{id='CompositeDiscoveryClient_SC-CONSUMER', uri=lb://SC-CONSUMER, order=0, predicate=org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$337/1295338046@f2ff0c8, gatewayFilters=[OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory$$Lambda$717/1168359877@1f74a2b, order=1}]} 2018-05-27 09:58:07.905 DEBUG 31658 --- [ctor-http-nio-2] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@f5bf288}, order=-1}, OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.RewritePathGatewayFilterFactory$$Lambda$717/1168359877@1f74a2b, order=1}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@26c77f54}, order=10000}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@4c38cd16}, order=10100}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@2c1d57bc}, order=2147483637}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@6e9a0bea}, order=2147483646}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@7ddcb0dc}, order=2147483647}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@3e856100}, order=2147483647}]
As can be seen from the console print log above, the time consumption corresponding to the URL is not printed.
2. Add the URL prefix / test/prefix / access, and the test URL is http://localhost:9000/test/prefix/hello/testCustomFilter/xujin
2018-05-27 10:01:56.057 DEBUG 31658 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:9000/test/prefix/hello/testCustomFilter/xujin] to Route{id='test_consumer_service', uri=lb://SC-CONSUMER, order=0, predicate=org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$337/1295338046@6dbaa72e, gatewayFilters=[OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory$$Lambda$340/1149217113@41fb2078, order=0}, org.xujin.sc.filter.CustomFilter@281885a3, OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory$$Lambda$342/1099925775@4705cae6, order=0}]} 2018-05-27 10:01:56.057 DEBUG 31658 --- [ctor-http-nio-2] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@f5bf288}, order=-1}, OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory$$Lambda$340/1149217113@41fb2078, order=0}, OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.AddResponseHeaderGatewayFilterFactory$$Lambda$342/1099925775@4705cae6, order=0}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@26c77f54}, order=10000}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@4c38cd16}, order=10100}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@2c1d57bc}, order=2147483637}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@6e9a0bea}, order=2147483646}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@7ddcb0dc}, order=2147483647}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@3e856100}, order=2147483647}, org.xujin.sc.filter.CustomFilter@281885a3] 2018-05-27 10:02:00.347 INFO 31658 --- [ctor-http-nio-8] o.s.cloud.gateway.filter.GatewayFilter : /hello/testCustomFilter/xujin: 859ms
As can be seen from the above log, / hello/testCustomFilter/xujin: 859ms, the prefix was removed during Gateway forwarding.
2.2 GlobalFilter
2.2.1 what is GlobalFilter
The GlobalFilter interface has the same signature as GatewayFilter. These are special filters that are conditionally applied to all routes. (This interface and usage are subject to change in future milestones).
Let us customize the implementation of GlobalFilter. GlobalFilter is a global Filter that acts on all routes.
public interface GlobalFilter { /** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
2.2.2 custom globalfilter
Here, AuthSignatureFilter is simply defined to implement GlobalFilter. The use scenario is that the Gateway checks the permission of the requested URL to judge whether the requested URL is a legal request. By obtaining the value corresponding to authToken in the request context from ServerWebExchange and processing null judgment, other check logic can be customized.
package org.xujin.sc.filter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * Call authentication */ @Component public class AuthSignatureFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("authToken"); if (token == null || token.isEmpty()) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return -200; } }
GlobalFilter is finished. Is that the problem? There are two ways to make it run and take effect in the Gateway. One is to add @ Component annotation directly, and the other is to configure this Bean in Spring Config, as shown below:
@Bean public AuthSignatureFilter authSignatureFilter(){ return new AuthSignatureFilter(); }
test
1. Visit http://localhost:9000/test/prefix/hello/testCustomFilter/xujin As shown below, 401 returns because authToken is empty