Zuul网关之自定义过滤器

API网关
01 Spring Cloud Gateway网关之快速上手
02 Spring Cloud Gateway网关之两种路由配置方式
03 Spring Cloud Gateway网关之跨域支持
04 Spring Cloud Gateway网关之自定义全局过滤器
05 Spring Cloud Gateway网关之自定义路由过滤器
06 Spring Cloud Gateway网关之自定义路由谓词工厂
07 Spring Cloud Gateway网关之超时时间配置
08 Spring Cloud Gateway网关之配置说明
09 Zuul网关之快速上手
10 Zuul网关之路由配置
11 Zuul网关之跨域支持
12 Zuul网关之超时时间配置
13 Zuul网关之自定义过滤器

一 Zuul过滤器了解

首先,我们需要了解Zuul的过滤器分类,之后才能判断我们需要为业务需要定制的过滤器选定对应的类型,Zuul过滤器主要分为以下几类:

  • pre过滤器:
    • ServletDetectionFilter:检测请求是否通过Spring Dispatcher。使用FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY的键设置布尔值。
    • FormBodyWrapperFilter:解析表单数据并为下游请求重新编码。
    • DebugFilter:如果设置了debug请求参数,则将RequestContext.setDebugRouting()RequestContext.setDebugReques()设置为true
    • PreDecorationFilter:根据提供的RouteLocator确定需要路由到哪里。它还为下游请求设置了各种与代理相关的标头。(@EnableZuulProxy专属会初始化的spring bean)
  • post过滤器:
    • SendResponseFilter:将代理请求的响应写入当前响应。
  • route过滤器:
    • SendForwardFilter:使用Servlet RequestDispatcher转发请求。转发位置存储在RequestContext属性FilterConstants.FORWARD_TO_KEY中。这对于转发到当前应用程序中的端点很有用。
  • error过滤器:
    • SendErrorFilter:如果RequestContext.getThrowable()不为null,则转发到/error(默认情况下)。您可以通过设置error.path属性来更改默认转发路径(/error)。
    • RibbonRoutingFilter:使用Ribbon,Hystrix和可插拔HTTP客户端发送请求。服务ID在RequestContext属性FilterConstants.SERVICE_ID_KEY中找到。此过滤器可以使用不同的HTTP客户端。(@EnableZuulProxy专属会初始化的spring bean)
      • Apache HttpClient:默认客户端。
      • Squareup OkHttpClient v3:通过在类路径上具有com.squareup.okhttp3:okhttp库并设置ribbon.okhttp.enabled=true来启用。
      • Netflix Ribbon HTTP客户端:通过设置ribbon.restclient.enabled=true启用。该客户端具有局限性,包括不支持PATCH方法,但是还具有内置的重试功能。
    • SimpleHostRoutingFilter:通过Apache HttpClient将请求发送到预定的URL。 URL可在RequestContext.getRouteHost()中找到。(@EnableZuulProxy专属会初始化的spring bean)

二 如何自定义过滤器

通过对系统已有的过滤器及其分类的了解,下面我们看看如何自定义过滤器。

  • 我们需要继承ZuulFilter抽象类,并重写以下四个方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /**
    * 指定过滤器类型
    */
    @Override
    public String filterType() {
    return FilterConstants.ROUTE_TYPE;
    }
    /**
    * 指定过滤器次序
    */
    @Override
    public int filterOrder() {
    // after PreDecorationFilter
    return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    }
    /**
    * 用于判断过滤器是否需要执行
    */
    @Override
    public boolean shouldFilter() {
    RequestContext ctx = RequestContext.getCurrentContext();
    // TODO 根据ctx 获取的信息去判断 或者 通过 ProxyRequestHelper 来判断 如下
    // proxyRequestHelper 通过@Autowired注入 或者 构造传入
    if (proxyRequestHelper.isIncludedHeader("XXXHeader")) {
    // 判断有 XXXHeader 请求头
    }
    }

    @Override
    public Object run() {
    // TODO 具体过滤器需要执行的逻辑
    }
  • 如果zuul网关自定义的过滤器仅想对某些路由生效,如果去配置?

    • 可以在zuul网关下扩展多一个属性,如下:

      1
      2
      3
      4
      5
      6
      7
      8
      zuul:
      routes:
      business-api:
      path: /business/** # 路由路径
      customSensitiveHeaders: true # 是否去除敏感头 true为去除
      sensitiveHeaders: Cookie,Set-Cookie # 需要去除的敏感头 去除了 即不会往下游转发
      url: http://localhost:9999/api # 需要转发的url
      customerParam: true # 用户自定义的扩展属性
    • 再通过扩展ZuulProperties读取到该属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      @ConfigurationProperties(prefix = "zuul")
      @AllArgsConstructor
      @NoArgsConstructor
      public class CustomZuulProperties {

      @Getter
      @Setter
      private Map<String, CustomZuulRoute> routes = new LinkedHashMap<>();

      @NoArgsConstructor
      @AllArgsConstructor
      public static class CustomZuulRoute extends ZuulProperties.ZuulRoute {
      @Setter
      @Getter
      private Boolean customerParam;
      }
      }
    • 再通过重写shouldFilter()方法判断是否使用该过滤器

      1
      2
      3
      4
      5
      6
      7
      8
      public boolean shouldFilter() {
      Route route = routeLocator.getMatchingRoute(
      RequestContext.getCurrentContext()
      .getRequest()
      .getRequestURI());
      Boolean customerFlag = customZuulProperties.getRoutes().get(route.getId()).getCustomerParam();
      return BooleanUtils.isTrue(customerFlag);
      }