Gateway过滤器

      据作用范围划分为 GatewayFilter 和 GlobalFilter,区别如下:

  • GatewayFilter:网关过滤器,通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局,作用在所有路由上。
  • GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过 GatewayFilterAdapter 包装成 GatewayFilterChain 可识别的过滤器,为请求业务以及路由的 URI 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上

  Spring Cloud Gateway 的 Filter 的生命周期只有两个:“pre” 和 “post”。

  • PRE: 过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

  根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter 和 Hystrix。

Path路由过滤器

       Path 路径过滤器可以实现 URL 重写,通过重写 URL 可以实现隐藏实际路径提高安全性,易于用户记忆和键入,易于被搜索引擎收录等优点

       RewritePath 网关过滤器工厂采用路径正则表达式参数和替换参数,使用 Java 正则表达式来灵活地重写请求路径

spring:
  application:
    name: gateway-server # 应用名称
  cloud:
    gateway:
      # 路由规则
      routes:
        - id: product-service           # 路由 ID,唯一
          uri: lb://product-service     # lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:                   # 断言(判断条件)
            # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/product/**, /api-gateway/**
          filters:                      # 网关过滤器
            # 将 /api-gateway/product/1 重写为 /product/1
            - RewritePath=/api-gateway(?<segment>/?.*), $\{segment}

          PrefixPath 网关过滤器工厂为匹配的 URI 添加指定前缀

spring:
  application:
    name: gateway-server # 应用名称
  cloud:
    gateway:
      # 路由规则
      routes:
        - id: product-service           # 路由 ID,唯一
          uri: lb://product-service     # lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:                   # 断言(判断条件)
            # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/**
          filters:                       # 网关过滤器
            # 将 /1 重写为 /product/1
            - PrefixPath=/product

  StripPrefix 网关过滤器工厂采用一个参数 StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数

spring:
  application:
    name: gateway-server # 应用名称
  cloud:
    gateway:
      # 路由规则
      routes:
        - id: product-service           # 路由 ID,唯一
          uri: lb://product-service     # lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:                   # 断言(判断条件)
            # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/**
          filters:                       # 网关过滤器
            # 将 /api/123/product/1 重写为 /product/1
            - StripPrefix=2

  AddRequestParameter 网关过滤器工厂会将指定参数添加至匹配到的下游请求中

spring:
  application:
    name: gateway-server # 应用名称
  cloud:
    gateway:
      # 路由规则
      routes:
        - id: product-service           # 路由 ID,唯一
          uri: lb://product-service     # lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:                   # 断言(判断条件)
            # 匹配对应 URI 的请求,将匹配到的请求追加在目标 URI 之后
            - Path=/api-gateway/**
          filters:                       # 网关过滤器
            # 将 /api-gateway/product/1 重写为 /product/1
            - RewritePath=/api-gateway(?<segment>/?.*), $\{segment}
            # 在下游请求中添加 flag=1
            - AddRequestParameter=flag, 1

自定义过滤器

  在spring Cloud Gateway中,过滤器需要实现GatewayFilter和Ordered2个接口。写一个RequestTimeFilter,代码如下:

@Slf4j
public class RequestTimeFilter implements GatewayFilter,Ordered {

    private final static 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) {
        return builder.routes()
                .route(r -> r.path("/movie/**")
                        .filters(f->f.hystrix(config -> config.setName("movie.service")
                                                              .setFallbackUri("forward:/movie/fallback"))
                                      .filter(new RequestTimeFilter()))
                        .uri(uri)).build();
    }

global filter

  GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。

  

   该GlobalFilter会校验请求中是否包含了请求参数“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。代码如下:


@Slf4j
public class TokenFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if(StringUtils.isEmpty(token)){
            log.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接口,然后根据ServerWebExchange获取ServerHttpRequest,然后根据ServerHttpRequest中是否含有参数token,如果没有则完成请求,终止转发,否则执行正常的逻辑。

  需要将TokenFilter在工程的启动类中注入到Spring Ioc容器中,代码如下:

@Bean
public TokenFilter tokenFilter(){
        return new TokenFilter();
}

 

参考:

     https://blog.csdn.net/forezp/article/details/85057268

     https://windmt.com/2018/05/08/spring-cloud-14-spring-cloud-gateway-filter/

  

posted on 2019-10-10 23:04  溪水静幽  阅读(530)  评论(0)    收藏  举报