Gateway自定义全局过滤器

一、Gateway全局过滤器

1、全局过滤器(Global Filters)简介

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
Gateway自定义全局过滤器_第1张图片

2、ForwardRoutingFilter

ForwardRoutingFilter全局转发过滤器。

ForwardRoutingFilter会查看 exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值(一个URI)。如果 URL有转发 scheme(例如forward:///localendpoint),Gateway使用 Spring DispatcherHandler来处理请求。请求 URL的路径部分被转发URL中的路径覆盖。未修改的原始URL将附加到ServerWebExchangeUtils中的列表中。

源码如下:

Gateway自定义全局过滤器_第2张图片

3、ReactiveLoadBalancerClientFilter

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<Void> 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<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        URI uri = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        ReactorLoadBalancer<ServiceInstance> 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<Void> 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;
	}
}

Gateway自定义全局过滤器_第3张图片

– 求知若饥,虚心若愚。

你可能感兴趣的:(#,Spring,Cloud,Gateway,Gateway自定义全局过滤器)