Gateway过滤器在实现方式上,有两种过滤器:
- GatewayFilter(局部过滤器/网关过滤器): 需要通过
spring.cloud.routes.filters
配置在具体的路由下,只作用在当前特定路由上,也可以通过配置 spring.cloud.default-filters让它作用于全局路由上。spring.cloud.gateway.default-filters
上会对所有路由生效也算是全局的过滤器;但是这些过滤器的实现上都是要实现GatewayFilterFactory接口。 - GlobalFilter(全局过滤器):
不需要再配置文件中配置,作用在所有的路由上
,最终通过 GatewayFilterAdapter包装成 GatewayFilterChain能够识别的过滤器。
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。Spring Cloud Gateway y也提供了几种全局过滤器,同时我们也可以自定义全局过滤器。
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
ForwardRoutingFilter全局转发过滤器。
ForwardRoutingFilter会查看 exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值(一个URI)。如果 URL有转发 scheme(例如forward:///localendpoint),Gateway使用 Spring DispatcherHandler来处理请求。请求 URL的路径部分被转发URL中的路径覆盖。未修改的原始URL将附加到ServerWebExchangeUtils中的列表中。
源码如下:
ReactiveLoadBalancerClientFilter全局负载均衡过滤器。
LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个URI)。如果该值的 scheme是 lb,比如:lb://app-order ,它将会使用 Spring Cloud的 LoadBalancerClient 来将 微服务名(本例中为app-order)解析成实际的 host和 port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容。未修改的原始URL将附加到ServerWebExchangeUtils中的列表中。
#配置 gateway网关
gateway:
#设置路由:路由id、路由到微服务的uri、断言
routes:
# app-order服务路由配置
- id: app-order #路由ID,全局唯一,建议配置服务名。
uri: lb://app-order #lb 整合负载均衡器ribbon,loadbalancer
predicates:
- Path=/order/** # 断言,路径相匹配的进行路由
源码如下:
public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
private static final Log log = LogFactory.getLog(ReactiveLoadBalancerClientFilter.class);
private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
private final LoadBalancerClientFactory clientFactory;
private LoadBalancerProperties properties;
public ReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
this.clientFactory = clientFactory;
this.properties = properties;
}
public int getOrder() {
return 10150;
}
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
}
return this.choose(exchange).doOnNext((response) -> {
if (!response.hasServer()) {
throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
} else {
ServiceInstance retrievedInstance = (ServiceInstance)response.getServer();
URI uri = exchange.getRequest().getURI();
String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance, overrideScheme);
URI requestUrl = this.reconstructURI(serviceInstance, uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
}
}).then(chain.filter(exchange));
} else {
return chain.filter(exchange);
}
}
protected URI reconstructURI(ServiceInstance serviceInstance, URI original) {
return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
}
private Mono choose(ServerWebExchange exchange) {
URI uri = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
ReactorLoadBalancer loadBalancer = (ReactorLoadBalancer)this.clientFactory.getInstance(uri.getHost(), ReactorServiceInstanceLoadBalancer.class);
if (loadBalancer == null) {
throw new NotFoundException("No loadbalancer available for " + uri.getHost());
} else {
return loadBalancer.choose(this.createRequest());
}
}
private Request createRequest() {
return ReactiveLoadBalancer.REQUEST;
}
}
更多全局过滤器查看官方文档。下面我们自定义一个全局过滤器。
二、自定义全局过滤器 1、创建自定义全局过滤器类在 Gateway服务中,创建自定义全局过滤器类必须实现 GlobalFilter接口
。每一个过滤器都必须指定一个int类型的 order值,order值越小,过滤器优先级越高,执行顺序越靠前。GlobalFilter通过实现 Ordered接口来指定 order值。
模拟一个登录的校验。基本逻辑:如果请求中有 token参数,则认为请求有效,放行。
@Component
@Slf4j
public class AppCheckAuthGatewayFilter implements GlobalFilter, Ordered {
/**
* 参考 GlobalFilter接口的实现类
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("---------执行全局过滤器-----------");
// 请求头或者请求参数中获取token
String token = exchange.getRequest().getHeaders().getFirst("token");
//String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
log.info("token is null");
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
// 401 用户没有访问权限
response.setStatusCode(HttpStatus.UNAUTHORIZED);
byte[] bytes = HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes();
DataBuffer buffer = response.bufferFactory().wrap(bytes);
// 请求结束,不继续向下请求
return response.writeWith(Mono.just(buffer));
}
// TODO 校验token进行身份认证
log.info("开始校验token,token={}", token);
return chain.filter(exchange);
}
/**
* 当有多个过滤器时, order值越小,越优先先执行
*
* @return
*/
@Override
public int getOrder() {
return 100;
}
}
– 求知若饥,虚心若愚。