spring-cloud(十二)GateWay强大的Filter(过滤)功能
花式玩转gateway的filter过滤功能
spring-cloud-Hoxton.SR6(十二)GateWay强大的Filter(过滤)功能
文章目录
- 一、GatewayFilter(内置过滤器的使用)
- 二、Gateway 全局Filter
- 三、结语与分享
前言…
在之前zuul篇我们已经初略的讲过一点,zuul的过滤器有四种…如下…
filterType: 该方法需要返回一个字符串来代表过滤器的类型,这个类型就是Zuul中的过滤器4种不同生命周期
pre
:在请求到达路由前被调用route
:在路由请求时被调用error
: 处理请求时发生的错误时被调用。post
:在route和error过滤器之后被调用,最后调用(路由到微服务以后执行)。
gateway 只有两种类型的过滤器:pre
和 post
。和zuul这两种呢,效果可以说是一样。
gateway 过滤器按照范围,又可以分为俩种:GatewayFilter
与 GlobalFilter
GlobalFilter
全局过滤器GatewayFilter
将应用到单个路由或者同一个分组中的路由上。
springcloud-gateway
包括许多内置的GatewayFilter工厂
接下来呢,咱们就较为细致的使用一些,内置的GatewayFilter工厂,以及使用全局过滤器进行微服务请求权限过滤演示…
一、GatewayFilter(内置过滤器的使用)
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。
下图,便是gateway中内置的过滤器了…
由于内置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
,查看,其打印了请求头信息
测试:
果然,其添加了一个请求头在我们的请求头中!!效果呢,已经出来了!
(2)路由predicates 和路由Filer 优先级问题?
这个时候呢,可能有小伙伴想要发问了!
前边讲路由谓词的时候,不是讲过请求头-谓词工厂
吗,这里怎么又来个请求头-filter
?,有何区别?
区别?
区别大了去了!!!
请求头-谓词工厂, 是当请求中携带某请求头时才会触发路由,是先于请求头-filter触发的
例如,我们如此配置 ,设置一个请求头谓词工厂,在设置一个符合要求的请求头filter猜猜结果?
喜闻乐见–404
所以呢!!
路由谓词的优先级是高于路由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
请求头给移除掉
(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
的参数
不过可惜的是,其不能使用@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
参数则就会被移除
(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
这里我提醒一下:
我所演示的,仅仅是配置了一条信息 ,例如 请求头 配置了一个,请求参数配置了一个…
但是实际上,你要知道,配置的是filter一个集合,那么元素就可以有多个,只要你所配置的参数名不同,或请求头 响应头的键不同,则是能配置多个的…
源码中的filters:
例如这样:
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
,但有的值又有些差别…
那么这个时候呢,我们就可以使用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
相同响应头名-保尾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
相同响应头-名值唯一
此种策略,则仅会在所有的的响应头名字与值一致是保留一个
完整配置如下:
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组合为唯一值,则会保留下来
(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
移除掉
(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);
}
我们当前的请求是由网关 >demo-order>demo-product ,在demo-product
服务中,仍能获取到token,所以呢gateway默认是可以将Authorization
请求头传递到下游服务器的…
那,我要演示个啥呢????
我要演示的是,我可以将一个请求头的值,追加到另一个请求头值下,且传递到下游服务器
MapRequestHeaderGatewayFilterFactory
工厂采用工厂采用fromHeader
和toHeader
参数。它将创建一个新的命名标头(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
存在时:
测试fromHeader
不存在时:
好办…tst-header
改个名字即可…
注意的是,MapRequestHeader
参数必须是两个header,如果仅设置一个header ,启动能正常启动,但如果请求触发到了配置的路由,则会报错!!!
(10)添加请求前缀-filter
PrefixPathGatewayFilterFactory
此filter 会为我们的请求额外再添加一个前缀…
就像之前的zuul配置…为所有的网关请求添加一个统一的前缀
和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
(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
再次访问,即可正常访问接口了…
此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
(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
以上的所有类型的过滤器呢,全是针对于某个服务的,每个服务亦可设置不同的过滤器,那么有的时候呢,我们又希望有一个牛逼的过滤器可以作用与所有的路由(服务),比如,请求前缀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
可以看到,我们的请求中携带了一个X-Response-Default:panghu
的响应头了…
那么,为何404了呢??
因为给我们加了一个请求前缀/lei-app
啊!!!!!
在开发中,我们可以根据默认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
则会放行…
当我们访问其他接口时,则需要我们进行登录…
给当前请求,添加请求头x-request-token
,然后进行访问
在实际开发中,我们可以在用户登录认证后,让其所有请求携带token, 对需要登录才可访问的接口进行校验即可…
… … … … … … … … … … … …
三、结语与分享
以上呢,仅仅是粗略的讲述了一下gateway的Filter 的使用
其实,官网的讲述也是非常的详细的…
附上官网地址:Gateway-官网
附上项目源码: spring-cloud-hoxton-sr6
更多推荐
所有评论(0)