为什么会出现
通常情况下,出于安全方面的考虑,服务端提供的服务往往都会有一定的校验逻辑,例如用户登陆状态校验、签名校验等。
在微服务架构中,系统由多个微服务组成,所有这些服务都需要这些校验逻辑,此时我们就可以将这些校验逻辑写到 Spring Cloud Gateway 的 Filter 过滤器中。
Filter 的分类
Spring Cloud Gateway 提供了以下两种类型的过滤器,可以对请求和响应进行精细化控制。
按照作用范围划分,Spring Cloud gateway 的 Filter 可以分为 2 类:
- GatewayFilter:应用在单个路由或者一组路由上的过滤器。
- GlobalFilter:应用在所有的路由上的过滤器。
GatewayFilter 网关过滤器
GatewayFilter 是 Spring Cloud Gateway 网关中提供的一种应用在单个或一组路由上的过滤器。它可以对单个路由或者一组路由上传入的请求和传出响应进行拦截,并实现一些与业务无关的功能,比如登陆状态校验、签名校验、权限校验、日志输出、流量监控等。
GatewayFilter 在配置文件(例如 application.yml)中的写法与 Predicate 类似,格式如下。
spring: cloud: gateway: routes: - id: xxxx uri: xxxx predicates: - Path=xxxx filters: - AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为 X-Request-Id 值为 1024 - PrefixPath=/dept #在请求路径前面加上 /dept ……
Spring Cloud Gateway 内置了多达 31 种 GatewayFilter,下表中列举了几种常用的网关过滤器及其使用示例。
网关过滤器示例
下面我们通过一个实例来演示 GatewayFilter 的配置,步骤如下。
1. 在 micro-service-cloud-gateway-9527 的 application.yml 中在添加一个动态路由,配置内容如下
- id: provider_dept_get_routh uri: lb://MICROSERVICECLOUDPROVIDERDEPT #使用服务名代替上面的具体带端口 predicates: - Path=/get/** filters: - PrefixPath=/dept #在请求路径上增加一个前缀 /dept
2. 重启 micro-service-cloud-gateway-9527,使用浏览器访“http://eureka7001.com:9527/get/1”,结果成功转发
GlobalFilter 全局过滤器
GlobalFilter 是一种作用于所有的路由上的全局过滤器,通过它,我们可以实现一些统一化的业务功能,例如权限认证、IP 访问限制等。当某个请求被路由匹配时,那么所有的 GlobalFilter 会和该路由自身配置的 GatewayFilter 组合成一个过滤器链。
Spring Cloud Gateway 为我们提供了多种默认的 GlobalFilter,例如与转发、路由、负载均衡等相关的全局过滤器。但在实际的项目开发中,通常我们都会自定义一些自己的 GlobalFilter 全局过滤器以满足我们自身的业务需求,而很少直接使用 Spring Cloud Config 提供这些默认的 GlobalFilter。
全局过滤器实例
1. 在 net.biancheng.c.filter 包下,新建一个名为 MyGlobalFilter 全局过滤器配置类,代码如下。
package net.biancheng.c.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Date; /** * 自定义全局网关过滤器(GlobalFilter) */ @Component @Slf4j public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("进入自定义的全局过滤器 MyGlobalFilter" + new Date()); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if (uname == null) { log.info("参数 uname 不能为 null!"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { //过滤器的顺序,0 表示第一个 return 0; } }
2. 重启 micro-service-cloud-gateway-9527,使用浏览器访问“http://eureka7001.com:9527/dept/list”,我们会发现访问报 406 错误,控制台输出如下。
2022-11-1 16:25:39.450 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : CST 2021进入自定义的全局过滤器 MyGlobalFilter 2022-11-1 16:25:39.451 INFO 19116 --- [ctor-http-nio-4] net.biancheng.c.filter.MyGlobalFilter : 参数 uname 不能为 null!
3. 使用浏览器访问“http://eureka7001.com:9527/dept/list?uname=123”,则会成功转发
Spring Cloud Gateway 工作流程
客户端将请求发送到 Spring Cloud Gateway 上。
Spring Cloud Gateway 通过 Gateway Handler Mapping 找到与请求相匹配的路由,将其发送给 Gateway Web Handler。
Gateway Web Handler 通过指定的过滤器链(Filter Chain),将请求转发到实际的服务节点中,执行业务逻辑返回响应结果。
过滤器之间用虚线分开是因为过滤器可能会在转发请求之前(pre)或之后(post)执行业务逻辑。
过滤器(Filter)可以在请求被转发到服务端前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等。
过滤器可以在响应返回客户端之前,对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。
响应原路返回给客户端。
总而言之,客户端发送到 Spring Cloud Gateway 的请求需要通过一定的匹配条件,才能定位到真正的服务节点。在将请求转发到服务进行处理的过程前后(pre 和 post),我们还可以对请求和响应进行一些精细化控制。
Predicate 就是路由的匹配条件,而 Filter 就是对请求和响应进行精细化控制的工具。有了这两个元素,再加上目标 URI,就可以实现一个具体的路由了。