Spring Cloud Alibaba - Gateway 入门案例(三)(断言 / 过滤器)(自定义 断言 / (全局 / 局部)过滤器)(非阿里组件)
回溯
在上一篇博客的 SpringCloud Gateway 讲解中 ,我们讲述到了,企业选择配置多的方式来使用 SpringCloud Gateway ,是因为这种方式比较灵活,之所以灵活,最最最主要,是体现在了两个参数上 断言(predicates)/ 过滤器(filters) 。
断言(predicates)
Spring Cloud Gateway matches routes as part of the Spring WebFlux HandlerMapping infrastructure. Spring Cloud Gateway includes many built-in route predicate factories. All of these predicates match on different attributes of the HTTP request. You can combine multiple route predicate factories with logical and statements.
Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。
断言就是说: 在 什么条件下 才能进行路由转发
SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配。具体如下:
- 基于Datetime类型的断言工厂,此类型的断言根据时间做判断,主要有三个:
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
例:Before=2020-07-09T00:00:00.000+08:00
- 基于远程地址的断言工厂 RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
例:-RemoteAddr=192.168.5.1/24
- 基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。
例:-Cookie=chocolate, ch.
- 基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
例:-Header=X-Request-Id, \d+
- 基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
例:-Host=**.testhost.org
- 基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
例:-Method=GET
- 基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
例:-Path=/foo/{segment}
- 基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
例:-Query=baz, ba.
这边附上文档地址,有需要可以去详细翻阅,博主的举例仅供参考。
点击 spring-cloud-gateway/2.2.3.RELEASE/reference/html/#gateway-request-predicates-factories 进入文档地址。
简单测试:配置文件如下
ps ( 此测试基于上一篇博文,若有疑问,请参考上一篇博文 )
server:
port: 7777
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #让gateway可以发现nacos中的微服务
routes: # 路由数组 指当请求满足什么样的条件的时候,转发到哪个微服务上
- id: nacosxfz_route #当前路由标识,要求唯一 (默认值uuid,一般不用,需要自定义)
uri: lb://test-scz #请求最终要被转发的地址 lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
order: 1 #路由优先级,数字越小,优先级越高
predicates: #断言 判断条件,返回值是boolean 转发请求要返回的条件 (可以写多个)、
- Path=/scz_server/** #当请求路径满足path指定的规则时,此路由信息才会正常转发
- Method=POST
filters: #过滤器(在请求传递过程中,对请求做一些手脚)
- StripPrefix=1 # 在请求转发之前去掉一层路径
nacos:
discovery:
server-addr: 127.0.0.1:8848
证明:当请求方法为Get的时候是无法成功地。
自定义断言
有很多情况下,这些断言并不能满足我们的业务需求,需要自定义断言。
比如,现在我希望参数中 name 带有 huge 或者 liuyifei 或者 pengyuyan 才能进行转发,那么就需要自定义断言。
首先需要在yam总添加自定义断言名称。
为了开发方便,添加插件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
然后添加一个配置类。
/**
* 这是一个自定义的路由断言工厂类,要求有两个
* 名字必须是 配置+RoutePredicateFactory 本类的配置字段是name
* 必须继承 AbstractRoutePredicateFactory <配置类>
*/
@Component
public class NameRoutePredicateFactory extends AbstractRoutePredicateFactory <NameRoutePredicateFactory.Config>{
public NameRoutePredicateFactory() {
super(NameRoutePredicateFactory.Config.class);
}
/**
* 读取配置文件的中参数值 给他赋值到配置类中的属性上
* @return 配置类的数据
*/
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("xianJian");
}
/**
* 断言逻辑的具体内容
* @param config 当前断言的配置读取
* @return 断言结果
*/
@Override
public Predicate<ServerWebExchange> apply(NameRoutePredicateFactory.Config config) {
return serverWebExchange -> {
String name = serverWebExchange.getRequest().getQueryParams().getFirst("name");
if(StringUtils.isNotBlank(name)){
List<String> nameList = Arrays.asList(config.xianJian.split("\\|"));
return nameList.contains(name);
}
return false;
};
}
/**
* 读取配置文件中的配置,按顺序配置参数
*/
@Data
@NoArgsConstructor
public static class Config {
private String xianJian;
}
}
ps: 这个类的名称 大有玄机 ,需要你自定义的属性名称 + RoutePredicateFactory 这个是固定用法
也可以参考:
这些类的编写,相信看过两个你就知道怎么自己编写了。
这时候进行测试,发现 name 带有 huge 或者 liuyifei 或者 pengyuyan 成功。
若条件不满足,无法转发,会报 404
过滤器(filters)
- 作用: 过滤器就是在请求的传递过程中,对请求和响应做一些手脚
- 生命周期: Pre Post
- 分类: 局部过滤器(作用在某一个路由上) 全局过滤器(作用全部路由上)
在Gateway中, Filter的生命周期只有两个:“pre” 和 “post”。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。
Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上。
局部过滤器
局部过滤器是针对单个路由的过滤器。
Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.
在SpringCloud Gateway中内置了很多不同类型的网关路由过滤器。具体如下:
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload TooLarge | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
详细文档可点击前往 spring-cloud-gateway/2.2.3.RELEASE/reference/html/#gatewayfilter-factories
简单测试:SetStatus :配置文件如下
server:
port: 7777
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #让gateway可以发现nacos中的微服务
routes: # 路由数组 指当请求满足什么样的条件的时候,转发到哪个微服务上
- id: nacosxfz_route #当前路由标识,要求唯一 (默认值uuid,一般不用,需要自定义)
uri: lb://test-scz #请求最终要被转发的地址 lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
order: 1 #路由优先级,数字越小,优先级越高
predicates: #断言 判断条件,返回值是boolean 转发请求要返回的条件 (可以写多个)、
- Path=/scz_server/** #当请求路径满足path指定的规则时,此路由信息才会正常转发
- Method=POST
filters: #过滤器(在请求传递过程中,对请求做一些手脚)
- StripPrefix=1 # 在请求转发之前去掉一层路径
- SetStatus=66666 #编辑返回的状态码为 6666
nacos:
discovery:
server-addr: 127.0.0.1:8848
测试成功
自定义局部过滤器
还是老问题,我们会需要自己的业务逻辑,那么内置的局部过滤器就无法满足我们的需求,我们需要自定义局部过滤器。
ps(这个和自定义断言极其相似)
现在有一个业务,需要我们手动配置,当访问某个微服务的时候,是否开启惊喜大礼包。
首先添加自定义的配置。
紧接编写配置类:
/**
* 这是一个自定义的过滤器工厂类,要求有两个
* 名字必须是 配置+GatewayFilterFactory 本类的配置字段是 Surprise
* 必须继承 AbstractGatewayFilterFactory <配置类>
*/
@Component
@Slf4j
public class SurpriseGatewayFilterFactory extends AbstractGatewayFilterFactory<SurpriseGatewayFilterFactory.Config> {
public SurpriseGatewayFilterFactory() {
super(SurpriseGatewayFilterFactory.Config.class);
}
/**
* 读取配置文件的中参数值 给他赋值到配置类中的属性上
*
* @return 配置类的数据
*/
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("flag");
}
/**
* 断言逻辑的具体内容
*
* @param config 当前断言的配置读取
* @return 过滤结果
*/
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (config.flag) {
log.info("仙剑惊喜大礼包- 土灵珠 ");
}
return chain.filter(exchange);
};
}
@Data
@NoArgsConstructor
public static class Config {
private boolean flag;
}
}
ps: 这个类的名称 大有玄机 ,需要你自定义的属性名称 + GatewayFilterFactory这个是固定用法
同样可以参考:
这些类的写法。
此时进行访问验证:
自定义过滤器验证成功。
全局过滤器
Global Filters
The GlobalFilter interface has the same signature as GatewayFilter. These are special filters that are conditionally applied to all routes.
我们其实已经简单的运用了 LoadBalancer 若有需要,可参考文档:
spring-cloud-gateway/2.2.3.RELEASE/reference/html/#global-filters 。
自定义全局过滤器
这个就经常用到了,比如说验证是否登入?
一般的 sso 会返回登入信息,验证token,设备信息等等,这边用最简单的 token 进行例子编写。
这时候配置文件无需修改,因为是全局配置。
添加一个实现类: 目标:让 Cookies 中 没有携带 token 的无法访问并且返回 401。
/**
* 自定义全局过滤器
* 必须实现 GlobalFilter, Ordered 接口
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
/**
* 用于编写过滤器逻辑
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//简单判断,仅用于测试,真实业务可别这么写
HttpCookie token = exchange.getRequest().getCookies().getFirst("token");
if(null == token){
//没有携带token返回 401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 用来标识当前用户的优先级,返回值越小,优先级越高
* @return 数值
*/
@Override
public int getOrder() {
return 0;
}
}
测试:
无携带token。