spring-cloud-Hoxton.SR6(十二)GateWay强大的Filter(过滤)功能


前言…

在之前zuul篇我们已经初略的讲过一点,zuul的过滤器有四种…如下…

filterType: 该方法需要返回一个字符串来代表过滤器的类型,这个类型就是Zuul中的过滤器4种不同生命周期

  • pre:在请求到达路由前被调用
  • route:在路由请求时被调用
  • error: 处理请求时发生的错误时被调用。
  • post:在route和error过滤器之后被调用,最后调用(路由到微服务以后执行)。

gateway 只有两种类型的过滤器:prepost和zuul这两种呢,效果可以说是一样。

gateway 过滤器按照范围,又可以分为俩种:GatewayFilterGlobalFilter

  • GlobalFilter 全局过滤器
  • GatewayFilter 将应用到单个路由或者同一个分组中的路由上。

springcloud-gateway包括许多内置的GatewayFilter工厂

接下来呢,咱们就较为细致的使用一些,内置的GatewayFilter工厂,以及使用全局过滤器进行微服务请求权限过滤演示…

一、GatewayFilter(内置过滤器的使用)

路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。

下图,便是gateway中内置的过滤器了…

image-20201018152103775

由于内置filter是针对于服务的,所以呢,我们结合上一篇的路由谓词来配置路由的filter,但仅仅只罗列一些常见以及 常用的内置Filter

(1)请求头-filter

AddRequestHeaderGatewayFilterFactory 此Filter会给我们请求添加一个我们的配置的请求头…

- AddRequestHeader=请求头,值

完整配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - AddRequestHeader=X-Request-lei,man

那么,如此配置呢,即会给我们请求信息中额外添加一组 name=X-Request-lei |vaue=man的请求头

还是以我们之前常用的测试接口http://localhost:6001/product/demo/1,查看,其打印了请求头信息

image-20201018163020126

测试:

image-20201018163048941

果然,其添加了一个请求头在我们的请求头中!!效果呢,已经出来了!

(2)路由predicates 和路由Filer 优先级问题?

这个时候呢,可能有小伙伴想要发问了!

前边讲路由谓词的时候,不是讲过请求头-谓词工厂吗,这里怎么又来个请求头-filter?,有何区别?

区别?

区别大了去了!!!

请求头-谓词工厂, 是当请求中携带某请求头时才会触发路由,是先于请求头-filter触发的

image-20201018164310483

例如,我们如此配置 ,设置一个请求头谓词工厂,在设置一个符合要求的请求头filter猜猜结果?

喜闻乐见–404

image-20201018164435753

所以呢!!

路由谓词的优先级是高于路由Filter的…

(3)移除请求头-filter

``RemoveRequestHeaderGatewayFilter`此配置会根据我们设置的请求头名进行移除

配置:

- RemoveRequestHeader=需要移除出的请求头名

详细配置:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - RemoveRequestHeader=X-Request-lei

例如:如此设置则会我们的请求中的X-Request-lei 请求头给移除掉

image-20201019221900726

(4)请求参数-filter

AddRequestParameterGatewayFilterFactory `此Filter 会默认给我们的请求携带一个请求参数,参数名和值需要自己指定

- AddRequestParameter=请求参数名,请求参数对应值

完整配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - AddRequestParameter=name,basketball

如此配置 那么我们的请求满足路由谓词条件Path=/product/**,/zs/{aa}时,则会给我们的请求携带一个名为:name 值为basketball的参数

image-20201018171121485

不过可惜的是,其不能使用@PathVariable 路径占位符,在我们微服务环境下,RestFul+@PathVariable 请求形式下,其可能发挥的范围有限…

(5)移除请求参数-filter

RemoveRequestParameterGatewayFilterFacoty 此filter会将我们的请求参数进行移除

配置:

- RemoveRequestParameter=需要移除的参数名

详细配置:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - RemoveRequestParameter=name

如此设置呢,只要是请求中的name参数则就会被移除

image-20201019222437978

(6)响应头-filter

AddResponseHeaderGatewayFilterFactory ` 此Filter 会为我们的请求结果填充响应头

- AddResponseHeader=响应头键,对应的值

完整配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - AddResponseHeader=x-response,leilei

如此设置呢,其会给我们添加一组响应头为:x-response 值为:leilei

image-20201018174146924

这里我提醒一下:

image-20201018174913544

我所演示的,仅仅是配置了一条信息 ,例如 请求头 配置了一个,请求参数配置了一个…

但是实际上,你要知道,配置的是filter一个集合,那么元素就可以有多个,只要你所配置的参数名不同,或请求头 响应头的键不同,则是能配置多个的…

源码中的filters:

image-20201018175037192

image-20201018175110029

例如这样:

filters:
  - AddRequestHeader=X-Request-lei,man
  - AddRequestParameter=name,basketball 
  - AddResponseHeader=x-response,lei
  - AddResponseHeader=x-server,lisi

那么,他则会给我们添加两个响应头信息一个请求参数信息,一个请求头信息…

(7)响应头去重-filter

在我们实际开发中可能存在下游服务器逻辑处理添加了响应头,可能gateway又统一添加了请求头,而且存在多个请求头键与值一致的情况下,造成响应头重复…

例如:我当前模拟的响应头重复情况如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei3
            - AddResponseHeader=x-response,lei3
            - AddResponseHeader=x-response,lei3

请求接口http://192.168.124.17:6001/product/demo/1 出现的响应头情况如下:

发现出现了重复的响应头x-response,但有的值又有些差别…

image-20201018205840050

那么这个时候呢,我们就可以使用gateway中的DedupeResponseHeaderGatewayFilter

DedupeResponseHeaderGatewayFilterFactory此请求可以跟我们的去重策略对响应头进行去重

核心配置:

- DedupeResponseHeader=需要去重的响应头名,多个用空格隔开,去重策略

由于我这里只是简单地demo,暂无法模拟上下游造成相同请求头情况

所以…我直接在gateway网关 使用响应头filter 手动的,添加几个相同名字的响应头…来模拟此效果

首先说明一下去重策略:

去重策略

#1.RETAIN_FIRST 此配置则只会保留相同响应头的第一个
#2.RETAIN_LAST  此配置则只会保留相同响应头的最后一个
#3.RETAIN_UNIQUE 此配置保留所有的唯一值,即 相同的响应头,相同的值 为唯一
相同响应头名-保头 RETAIN_FIRST

完整配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            # 添加响应头配置
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei3
            #去重响应头配置 保留第一个
            - DedupeResponseHeader=x-response,RETAIN_FIRST

如此配置的话,仅仅只会保留一个第一个响应头x-response:lei1

image-20201018215531511

相同响应头名-保尾RETAIN_LAST
spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            # 添加响应头配置
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei3
            #去重响应头配置 最后一个
            - DedupeResponseHeader=x-response,RETAIN_LAST

如此配置则只会保留一个名为x-resonse的响应头,且仅保留最后一个。我这里则是x-response:lei3

image-20201018220151061

相同响应头-名值唯一

此种策略,则仅会在所有的的响应头名字与值一致是保留一个

完整配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            # 添加响应头配置
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei2
            - AddResponseHeader=x-response,lei3
            - AddResponseHeader=x-response,lei3
            - AddResponseHeader=x-response,lei3
            #去重响应头配置 响应头与值,组合唯一
            - DedupeResponseHeader=x-response,RETAIN_UNIQUE

如此配置的话,那么则会在我们设置的7个x-response中保留三个 分贝是x-response:lei1 x-response:lei2 x-response:lei3 ,因为这三组,headerName headerValue组合为唯一值,则会保留下来

image-20201018220654277

(8)移除响应头-filter

RemoveResponseHeaderGatewayFilter 此Filter 设置后,如果响应头中有与我们设置的响应头名字相等,则会将其进行移除…

配置:

- RemoveResponseHeader=需要移除的响应头

详细配置:

spring:
  cloud:
    gateway:
      routes:
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            # 添加响应头配置
            - AddResponseHeader=x-response,lei1
            - AddResponseHeader=x-response,lei2
            # 测试是偶移除响应头x-response
            - RemoveResponseHeader=x-response

如此设置呢,则会将下游的响应头x-response 移除掉

image-20201019222946113

(9)MapRequestHeader-filter

还记得在zuul篇演示过,我们的服务有时候需要拿到token请求头,然后快速的获取当前操作用户信息,方便咱们自己的业务逻辑处理…

点击:到我的zuul篇…可查看zuul如何处理请求头丢失问题:

那么,我们的gateway 是否可以直接将我们的请求头信息,传递给下游服务器呢??

咱们来测试一下:

首先,改造下游demo-product服务接口,使其接收请求头信息

    @GetMapping("/findProductByName")
    public Result findProductByName(@RequestHeader(name = "x-token",required = false)String token,String name) {
        return Result.success("商品查询成功:当前商品服务Ip 端口为" + ip + ":" + port, "当前查询商品名为:"+name+"当前请求头token="+token);
    }

image-20201018231410258

我们当前的请求是由网关 >demo-order>demo-product ,在demo-product服务中,仍能获取到token,所以呢gateway默认是可以将Authorization 请求头传递到下游服务器的…

那,我要演示个啥呢????

我要演示的是,我可以将一个请求头的值,追加到另一个请求头值下,且传递到下游服务器

MapRequestHeaderGatewayFilterFactory工厂采用工厂采用fromHeadertoHeader参数。它将创建一个新的命名标头(toHeader),然后fromHeader从传入的http请求中将其值从现有的命名标头()中提取出来。如果输入标头不存在,则过滤器不起作用。如果新的命名头已经存在,则其值将使用新值进行扩充。

配置:

- MapRequestHeader=fromHeader, toHeader

什么意思呢,即当请求时 含有fromHeader ,那么则会吧他的值追加到toHeader然后传递到下游 ,如果fromHeader 不存在,则直接将toHeader 请求头信息传递到下游

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
            - MapRequestHeader=test-header, Authorization

如此配置呢,则如果test-header 请求头存在,则会将其值添加到Authorization 请求头值得后边,并传递到下游…如不存在,则直接将请求头Authorization 传递到下游

我这里直接由网关请求到demo-product服务了

测试fromHeader 存在时:

image-20201018232525591

测试fromHeader 不存在时:

好办…tst-header改个名字即可…

image-20201018232929256

注意的是,MapRequestHeader 参数必须是两个header,如果仅设置一个header ,启动能正常启动,但如果请求触发到了配置的路由,则会报错!!!

(10)添加请求前缀-filter

PrefixPathGatewayFilterFactory 此filter 会为我们的请求额外再添加一个前缀…

就像之前的zuul配置…为所有的网关请求添加一个统一的前缀

image-20201019210507942和zuul不同的是,我们的PrefixPathGatewayFilterFactory 是在请求时自己给你在url前加一个前缀,而zuul此种配置呢,则是必须你的请求包含自己设置的请求前缀后,再匹配你的路由谓词…详情…请看我的zuul篇…

回过头来,咱们再来掰扯掰扯gateway的请求前缀filter

配置如下:

- PrefixPath=设置的请求前缀

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
          	#这里设置你要配置的url请求前缀...
            - PrefixPath=/lei

如此配置呢,咋会在我们的url前加上一个前缀/lei ; 例如,我现在访问的是http://192.168.124.17:6001/product/findProductByName?name=basketball 那么设置此filter后呢,其会给我们吧url变为http://192.168.124.17:6001/lei/product/findProductByName?name=basketball

他会在原本的url前携带我们设置的请求前缀…即/product/findProductByName?name=basketball 变为/lei/product/findProductByName?name=basketball

image-20201019211445610

(11)去除请求前缀-filter

就以我们上方的添加请求前缀示例:在添加了- PrefixPath=/lei 后呢,我们只要是触发到server-product断言- Path=/product/**,/zs/{aa} 的所有请求都会为我们添加上一个请求前缀/lei ,这也导致了我们的正常请求服务中有的接口404问题…要是想正常访问我们的url呢,则必须将前缀/lei 移除

接下来,咱们就来演示一下去除请求前缀filter的使用…

StripPrefixGatewayFilterFactory 可以设置一个parts参数,指定移除几位url

例如:设置为2 则会从url首个/开始 共移除两个/, 比如通过网关的请求Url为/name/blue/red则会移除两位Url 变为:/red

配置:

- StripPrefix=需要移除url位数

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
          	#要添加的url请求前缀...
            - PrefixPath=/lei
            # 需要移除url位数
            - StripPrefix=1

image-20201019213251583

再次访问,即可正常访问接口了…

此Filter呢,也正和我们前边Gateway基础篇提过一嘴的,zuul网关移除代理前缀功能一致…(详见zuul篇)

而在实际开发中呢,一般也是path断言谓词 结合 StripPrefix 移除请求前缀url组合配置使用最多了

例如下方这般配置:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: dodo_task
          uri: lb://dodo-task-web
          predicates:
            - Path=/tasks/**
          filters:
            - StripPrefix=1
        - id: dodo_user
          uri: lb://dodo-user-web
          predicates:
            - Path=/users/**
          filters:
            - StripPrefix=1
        - id: dodo_pay
          uri: lb://dodo-pay
          predicates:
            - Path=/pay/**
          filters:
            - StripPrefix=1

(12)替换请求头值-filter

``SetRequestHeaderGatewayFilterfactory` 该GatewayFilter会吧下游中与设置的请求头一致的值给替换为设置的值…

- SetRequestHeader=请求头, 替换的值

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
          	- SetRequestHeader=X-Request-lei, gold

那么如此设置呢,则会将下游所有的请求头X-Request-lei 的值设置为gold
image-20201019215409497

(13)替换响应头值-filter

吧下游与设置的响应头名一致的值进行替换

 SetResponseHeader=响应头, 替换的值

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
          	#测试响应头值是否被替换
          	- SetRequestHeader=X-Request-lei, zsls

如此设置则会把下游所有响应头X-Request-lei 的值替换为zsls

(14)强制更改响应码-filter

SetStatusGatewayFilterFactory 此Filter 会强制将响应的HTTP状态都更改设置值,我们网关filter设置的值必须是有效的HttpStatus,它可以是整数值404或枚举的字符串表示形式:NOT_FOUND

配置:

- SetStatus=有效的HttpStatus响应码或枚举字符串

详细配置:

spring:
  cloud:
    gateway:
      routes:
        #根据服务名路由
        - id: server-product
          uri: lb://demo-product
          predicates:
            - Path=/product/**,/zs/{aa}
          filters:
          	#测试响应码
          	- SetStatus=401

image-20201019221128692


以上的所有类型的过滤器呢,全是针对于某个服务的,每个服务亦可设置不同的过滤器,那么有的时候呢,我们又希望有一个牛逼的过滤器可以作用与所有的路由(服务),比如,请求前缀Filter…我们如果每一个服务都设了一个请求前缀,那么前端访问后端的接口可能是五花八门的…这个时候呢,我们迫切希望前端访问后端的接口都有一个公共的前缀,例如项目名之类的…是否可以呢??答案是肯定的

(15)默认-filter

default-filters作用于所有的路由…强大朴实无华

我们基于此特性,可以设置一些公共的操作,例如统一请求前缀,统一请求头添加,统一响应头添加…等等

配置:

spring.cloud.gateway.default-filters

详细配置:

spring:
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=v, panghu
        - PrefixPath=/lei-app
      routes:
      .............

default-filters 与routes 是平级关系,在配置yml的时候,切记注意缩进哟!

如上方配置呢,则会在我们的所有请求中设置一个公共的请求前缀/lei-app 也会设置一个响应头default-filters:panghu

image-20201019224245664

可以看到,我们的请求中携带了一个X-Response-Default:panghu的响应头了…

那么,为何404了呢??

因为给我们加了一个请求前缀/lei-app啊!!!!!

image-20201019224434464

在开发中,我们可以根据默认Filter的特性,来做一些公共的事情…

(16)代码方式配置Filter

@Configuration
public class GateWayRouteConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("easy-order", r -> r.path("/order/**")
                        .filters(f->f.prefixPath("/order"))
                        .uri("http://localhost:9002/"))
                .route("server-product", r -> r.path("/product/**")
                        .filters(f->f.prefixPath("/product"))
                        .uri("lb://demo-product"))
                .build();
    }
}

二、Gateway 全局Filter

我们微服务中呢,可能需要在网关处做一个权限校验,这个时候呢,就可以使用我们gateway的全局过滤器了,

全局过滤器咱们需要实现两个接口 一个是GlobalFilter 一个是Ordered

实现Ordered主要是对我们的全局过滤器做一个优先级设置,数字越小,优先级越高

实现``GlobalFilter则是在filter中做一个逻辑操作,然后选择是否将请求提交给下一个过滤器或者输出响应请求…

下边,咱们来模拟一个登陆校验

简单代码如下:

import com.alibaba.fastjson.JSON;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author lei
 * @version 1.0
 * @date 2020/10/20 22:18
 * @desc 全局filter 模拟token验证
 */
@Component
public class AuthLoginConfig implements GlobalFilter, Ordered {
    /**
     * filter 做业务逻辑处理,选择将请求是否交由下一个过滤器或者响应出去
     * @param exchange
     * @param chain
     * @return
     */
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if ("/product/findProductByName".equals(exchange.getRequest().getURI().getPath())) {
            //一些特定的Url 放行
            return chain.filter(exchange);
        }
        String token =exchange.getRequest().getHeaders().getFirst("x-request-token");
        //根据自己需,验证对比token
        if(token == null) {
            byte[] bytes = JSON.toJSONString(Result.failure("需要登录", -2)).getBytes(StandardCharsets.UTF_8);
            ServerHttpResponse response = exchange.getResponse();
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
            //响应出去
            return response.writeWith(Mono.just(buffer));
        }
        //如果存在,则放行交给下个filter
        return chain.filter(exchange);
    }

    /**
     * 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
     */
    public int getOrder() {
        return 0;
    }
}

当我们访问的url path 为/product/findProductByName 则会放行…

image-20201020224817924

当我们访问其他接口时,则需要我们进行登录…

image-20201020224932647

给当前请求,添加请求头x-request-token,然后进行访问

image-20201020225103488

在实际开发中,我们可以在用户登录认证后,让其所有请求携带token, 对需要登录才可访问的接口进行校验即可…

… … … … … … … … … … … …

三、结语与分享

以上呢,仅仅是粗略的讲述了一下gateway的Filter 的使用

其实,官网的讲述也是非常的详细的…

附上官网地址:Gateway-官网

附上项目源码: spring-cloud-hoxton-sr6

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐