Gateway基本用法

932 阅读4分钟

一、Route

spring:
  cloud:
    gateway:
      routes:
      - id: after_route                 #路由ID需要保证唯一       
        uri: https://example.org        #匹配后路由的服务地址
        predicates:                     #请求转发判断条件
        - Path=/admin/**                #匹配对应的URL请求,会将匹配到的请求追加到目标uri
  • routes指的是配置路由转发规则,可以配置多个路由规则
  • 每个route有一个id,标识该路由的唯一性
  • uri指请求转发的目标
  • predicates指请求转发的判断条件

上述路由配置含义是当我们访问:http://gateway-ip:gateway-port/admin/** 时会被路由到example.org/admin/**

二、Predicate

2.1 Predicate基本用法

Predicate就是一组匹配规则,方便让请求根据匹配到的路由Route进行处理

image.png

Predicate介绍:Gateway Predicate

2.2 自定义Predicate Factory

spirng官方为我们提供了许多Predicate Factory能够满足我们日常开发过程中的大部分场景需求,但匹配条件比较复杂时就需要我们自定义实现。

步骤:

  • 自定义路由Predicate需要继承AbstractRoutePredicateFactory类,重写apply方法逻辑
  • 在apply方法中可获取ServerHttpRequest从而可以获取到请求信息
  • 类的命名需要以RoutePredicateFactory结尾,比如RbacAuthRoutePredicateFactory,那么在配置文件中使用时,RbacAuth就是该Predicate Factory的名称
@Component
public class RbacAuthRoutePredicateFactory
        extends AbstractRoutePredicateFactory<RbacAuthRoutePredicateFactory.Config> {


  public RbacAuthRoutePredicateFactory() {
    super(Config.class);
  }

  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
    return exchange -> {
      String requestURI = exchange.getRequest().getURI().getPath();
      if (config.getFlag().equals("rbac")
        &&(requestURI.startsWith("/sysuser")
          ||requestURI.startsWith("/sysorg")
          ||requestURI.startsWith("/sysrole")
          ||requestURI.startsWith("/sysmenu")
          ||requestURI.startsWith("/sysdict")
          ||requestURI.startsWith("/sysapi"))) {

        return true;  //表示匹配成功
      }
      return false;   //表示匹配失败
    };
  }
  //自定义参数args配置类
  public static class Config {
    private String flag; //该参数对应配置文件的args

    public String getFlag() {
      return flag;
    }

    public void setFlag(String flag) {
      this.flag = flag;
    }
  }
}
spring:
  application:
    name: zimug-server-gateway
  cloud:
    gateway:
      routes:
        - id: rbsc-service
          uri: http://localhost:8401/
          predicates:
            - name: RbacAuth
              args:
                flag: rbac

三、Filter

3.1 过滤器简介

通过简介我们知道,gateway内维护了一组过滤器链对请求进行一些过滤操作,比如:鉴权后添加Header携带令牌等。在过滤器中我们可以

  • 为请求增加请求头、增加请求参数、增加响应头等功能
  • 鉴权、记录审计日志、统计请求响应时长等和具体业务无关的重复性操作。

3.2 过滤器生命周期

Spring Cloud Gateway Filter生命周期:pre和post

image.png

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

3.3 过滤器分类

Gateway Filter 官方为我们提供了许多过滤器可以修改请求头、参数、路径等操作,一般我们也可以通过自定义过滤器实现这一操作

3.4 自定义全局过滤器

官方为我们提供了许多类型的过滤器,但在实际开发中这些过滤器可能并不能满足我们的实际开发场景,这时我们就需要创建个性化的过滤器实现特定功能。

要实现全局过滤器我们只要实现Global Filter、Order接口重写filter方法即可。

自定义过滤器实现接口耗时统计:

@Slf4j
@Component
public class interfaceTimeStatFilter implements  GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long startTime = System.currentTimeMillis();
        return chain.filter(exchange).then().then(Mono.fromRunnable(() -> {
            Long endTime = System.currentTimeMillis();
            log.info(exchange.getRequest().getURI().getRawPath() + ", cost time : " + (endTime - startTime) + "ms");
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

我们通过自定义全局过滤器还可以做许多操作,例如鉴权记录日志等。

3.5 自定义局部过滤器

比如在我们系统中有个别服务是专门为财务专员使用。我们假设有这样的需求,只有指定客户端ip可以访问财务系统。要如何实现呢?

  • 自定义Filter工厂需要继承AbstractGatewayFilterFactory类,重写apply方法逻辑
  • 在apply中可通过exchage.getRequest拿到ServerHttpRequest对象,从而可以获取请求信息
  • 在apply中可通过chain操作过滤器链
  • 类的命名需要以GatewayFilterFactory结尾,比如IPForbidGatewayFilterFactory,那么在配置文件中使用该Filter时IPForbid就是这个Filter工厂的名称
  • Config类可以定义一个或多个属性,需要重写List shortcutFieldOrder()这个方法指定属性名称
@Component
@Order(99)
public class IPForbidGatewayFilterFactory
    extends AbstractGatewayFilterFactory<IPForbidGatewayFilterFactory.Config> {

    public IPForbidGatewayFilterFactory()
    {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder()
    {
        return Arrays.asList("permitIp");  //对应config类的参数
    }

    @Override
    public GatewayFilter apply(Config config)
    {
        return (exchange, chain) -> {
            //获取服务访问的客户端ip
            String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
            if (config.getPermitIp().equals(ip)) {
                //如果客户端ip=permitIp,继续执行过滤器链允许继续访问
                return chain.filter(exchange);
            }
            //否则返回,拒绝请求
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            return exchange.getResponse().setComplete();
        };
    }

    static public class Config
    {
        private String permitIp;

        public String getPermitIp() {
            return permitIp;
        }

        public void setPermitIp(String permitIp) {
            this.permitIp = permitIp;
        }
    }
}

配置文件,因为只有一个参数所以图中192.168.1.6 将赋值给config类唯一参数:permitlp image.png

那么如何为GatewayFilterFactory配置多个参数呢?

首先Config要有多个成员变量,如:permitlp、xxx、其次配置文件要进行如下配置

image.png

文章参考: