Spring Cloud GateWay-过滤器

说起过滤器可能有不少实现方式,比如spring自带的就可以实现Spring Boot实战-配置过滤器的两种方式

这篇文章说的是Spring Cloud GateWay-过滤器,因为由于微服务的兴起,现在各个项目都会采用网关进行单独的资源隔离、token校验等,而不是放在下游的业务服务进行处理

Gateway 过滤器总结

filter的作用和生命周期

Zuul1.x 阻塞式IO 2.x 基于Netty,Spring Cloud GateWay天生就是异步非阻塞的,基于Reactor模型;

一个请求–>网关根据特定的条件匹配—>匹配成功之后可以将请求转发到指定的服务地址;在这个过程中,我们可以进行一些比较具体的控制(限流、日志、黑白名单)

路由(route): 网关最基础的部分,也是网关比较基础的工作单元。路由由多个ID、多个多标URL(最终路由到的地址)、一系列的断言(匹配条件判断)和Filter过滤器(精细化控制)组成。如果断言为true,则匹配该路由。

断言(predicates):参考了Java8中的断言java.util.function.Predicate,开发人员可以匹配Http请求中的所有内容(包括请求头、请求参数等)(类似于nginx中的location匹配),如果断言与请求相匹配则路由。

过滤器(filter):一个标准的Spring webFilter,使用过滤器,可以在请求之前或者之后执行业务逻辑

Predicates断言就是我们的匹配条件,Filter就可以理解为多个无所不能的拦截器,有了这两个元素,结合⽬标URL,就可以实现一个具体的路由转发。

作用

在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gatewat服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。

生命周期

Spring Cloud Gateway同zuul类似,有“pre”和“post”两种方式的filter

在这里插入图片描述

客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或者之后(post)执行业务逻辑。

Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、日志的输出、流量监控等。

从过滤器生命周期(影响时机点)的⻆度来说,主要有两个pre和post:

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

与zuul不同的是,filter除了分为“pre”和“post”两种方式的filter外,在Spring Cloud Gateway中,

从过滤器作用范围的角度来说,可分为另外两种,一种是针对于单个路由的gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的global gateway filer。现在从作用范围划分的维度来讲解这两种filter。

gateway filter

GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。

gateway内置的filter

在这里插入图片描述

自定义filter

自定义过滤器工厂

在上面的自定义过滤器中,有没有办法自定义过滤器工厂类呢?这样就可以在配置文件中配置过滤器了。现在需要实现一个过滤器工厂,在打印时间的时候,可以设置参数来决定是否打印请参数。查看GatewayFilterFactory的源码,可以发现GatewayFilterfactory的层级如下:

在这里插入图片描述
过滤器工厂的顶级接口是GatewayFilterFactory,我们可以直接继承它的两个抽象类来简化开发AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,这两个抽象类的区别就是前者接收一个参数(像StripPrefix和我们创建的这种),后者接收两个参数(像AddResponseHeader)。

过滤器工厂的顶级接口是GatewayFilterFactory,有2个两个较接近具体实现的抽象类,分别为AbstractGatewayFilterFactory和AbstractNameValueGatewayFilterFactory,这2个类前者接收一个参数,比如它的实现类RedirectToGatewayFilterFactory;后者接收2个参数,比如它的实现类AddRequestHeaderGatewayFilterFactory类。

可以参考gateway内置的Filter例如RedirectToGatewayFilterFactory的写法,

  • extends AbstractGatewayFilterFactory类,实现apply方法;
  • 需要在工程的启动文件Application类中,向Srping Ioc容器注册RequestTimeGatewayFilterFactory类的Bean;
  • 配置文件中配置,配置时只需要填写xxxGatewayFilterFactory前面的xxx;或者通过编码方式:例如:
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/serviceApi/**").filters(f -> f.filter(new MonitoringGatewayFilter()))
                        .uri("lb://tag"))
                .route(r -> r.path("/offline/**").filters(f -> f.filter(new MonitoringGatewayFilter()))
                        .uri("lb://claimplat-offline"))
                .route(r -> r.path("/test/**").filters(f -> f.filter(new MonitoringGatewayFilter()))
                        .uri("lb://claimplat-offline"))

                .route(r -> r.order(11000).path("/baidu")
                        .filters(f -> f.addRequestHeader("x-request-uuid", UUID.randomUUID().toString())
                                .filter(new MonitoringGatewayFilter()))
                        .uri("https://www.baidu.com"))
                .build();
    }

过滤器要求:

在过滤器(MyParamGatewayFilterFactory)中将http://localhost:10010/api/user/8?name=xxx中的参数name的值获取到并输出到控制台;并且参数名是可变的,也就是不一定每次都是name;需要可以通过配置过滤器的时候做到配置参数名。

1,自定义过滤器命名规则 :XXXXGatewayFilterFactory
2,创建一个静态内部类Config ,里面的属性为配置文件中配置的参数
​ - 过滤器名称=参数1,参数2…
3,extends AbstractGatewayFilterFactory<创建的静态内部类>
4,重写**shortcutFieldOrder()**方法
返回List参数列表为Config中属性集合
return Arrays.asList(“参数1”,参数2…);
5,无参构造方法中
super(Config.class);
6.编写过滤逻辑 public GatewayFilter apply(Config config)

@Override
public GatewayFilter apply(Config config) {
    return ((exchange, chain) -> {
       		//前置过滤器代码逻辑
       return chain.filter(exchange);//执行请求
		//后置过滤器代码逻辑
   });
}

注意:不要重写其他方法 ,会导致异常Unable to find GatewayFilterFactory with name xxx
7.application.yml配置过滤器

filters:
 - MyParam=name

下图为 shortcutFieldOrder()方法返回的集合为Config的属性名称集合
在这里插入图片描述

完整代码:

@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
    public MyParamGatewayFilterFactory() {
        super(MyParamGatewayFilterFactory.Config.class);
    }
    
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param");
    }

    @Override
    public GatewayFilter apply(Config config) {
    // 这里的config很重要指的是注册中心里面的配置,里面是key-value键值对
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            if (request.getQueryParams().containsKey(config.param)) {
                System.out.println("局部过滤器获取到的参数:" + config.param + "=" + request.getQueryParams().get(config.param).get(0));
            }

            return chain.filter(exchange);

        });
    }

    public static class Config {
        //对应在配置文件中配置过滤器的时候指定的参数名列表
        private String param;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }
    }
}

PS:这里的config很重要指的是注册中心里面的配置,里面是key-value键值对,key就是filter下面的args下面的ignorePath,value就是下面的值,所以返回是一个list集合,这个地方也就是解释了自定义网关过滤器的使用场景了,因为每个服务的制定的filter不同,那么就会有不同的过滤逻辑,如果不指定 那就使用全局过滤器,这里使用的是Hystrix
在这里插入图片描述

Global Filter

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

过滤器要求:

编写全局过滤器,在过滤器中检查请求中是否携带token请求头。如果token请求头存在则放行;如果token为空或者不存在则设置返回的状态码为:未授权也不再执行下去.

编写一个类 ,实现 两个接口: GlobalFilter, Ordered
GlobalFilter:全局过滤拦截器
Ordered:拦截器的顺序,数字越低,优先级越高
-----------------全局过滤器不用配置 --------------

@Component
public class MyGlobalFilter implements GlobalFilter,Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        boolean token = exchange.getRequest().getHeaders().containsKey("token");
        System.out.println("----全局过滤器token----"+token);
        if (!token){
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            ServerHttpResponse response = exchange.getResponse();
            return  response.setComplete();
        }
        return chain.filter(exchange);

    }

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

gateway内置的Global Filter

Spring Cloud Gateway框架内置的GlobalFilter如下:

在这里插入图片描述

自定义Global Filter

一般情况下GlobalFilter全局过滤器是程序员使用较多的过滤器;可以用来自定义一些黑名单校验、Token校验等。

步骤:

  • implements GlobalFilter, Ordered,实现filter方法;
  • 需要在工程的启动文件Application类中,向Srping Ioc容器注册GlobalFilter类的Bean;
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值