过滤器的作用:过滤器就是在请求的过程中,对请求和响应做一些手脚
生命周期:Pre Post
分类:局部过滤器(作用在某一个路由上) 全局过滤器(作用在全部路由上)
在Gateway中,Filter的生命周期只有两个:“pre”和“post”。
-
PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
-
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
目录
1 局部过滤器
局部过滤器是针对单个路由的过滤器。
1.1 内置局部过滤器
在SpringCloud Gateway中内置了很多不同类型的网关路由过滤器。具体如下:
1.2 内置过滤器的使用
下面示例SetStatus过滤器工厂的使用:
第一步:配置文件中加入过滤器
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务端地址
gateway:
discovery:
locator:
enabled: true # 启用探测器,让gateway可以发现nacos中的微服务
routes: # 路由数组(路由:就是当指定请求满足什么条件的时候,转发到哪个微服务)
- id: product_route # 当前路由的标识,要求唯一。默认uuid
uri: lb://service-product # lb指的是负载均衡(load balancing),service-product是nacos中微服务的名称
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径
- SetStatus=250 # 修改原始响应的状态码
第二步:启动测试
1.3 自定义局部过滤器
需求:通过过滤器,配置是否在控制台输出日志信息,以及是否记录日志。
第一步:在配置文件中,添加一个Log的过滤器配置
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos服务端地址
gateway:
discovery:
locator:
enabled: true # 启用探测器,让gateway可以发现nacos中的微服务
routes: # 路由数组(路由:就是当指定请求满足什么条件的时候,转发到哪个微服务)
- id: product_route # 当前路由的标识,要求唯一。默认uuid
uri: lb://service-product # lb指的是负载均衡(load balancing),service-product是nacos中微服务的名称
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径
- Log=true,false # 控制日志是否开启
第二步:自定义一个过滤器工厂,实现里面的方法
package cn.jack.filter;
import lombok.Data;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config>{
public LogGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("consoleLog", "cacheLog");
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
if (config.consoleLog) {
System.out.println("console日志已开启...");
}
if (config.cacheLog) {
System.out.println("cache日志已开启...");
}
return chain.filter(exchange);
});
}
@Data
public static class Config{
private boolean consoleLog;
private boolean cacheLog;
}
}
第三步:运行测试
2 全局过滤器
全局过滤器作用于所有路由,无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。
2.1 内置的全局过滤器
SpringCloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理的。
2.2 自定义全局过滤器
示例通过自定义全局过滤器,完成统一的权限校验。
开发中的鉴权逻辑:
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务端对token进行解密,判断是否有效。
如下图,对于验证用户是否已经登录及鉴权的过程,可以在网关统一校验。
校验的标准就是请求中是否携带token凭证,以及token的正确性。
下面我们通过自定义一个GlobalFIlter,去校验所有请求的请求参数中是否包含“token”,如果不包含请求参数“token”则不转发路由,否则执行正常逻辑。
package cn.jack.filter;
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.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义全局过滤器,需要实现GlobalFilter和Ordered接口
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
// 完成鉴权逻辑
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isEmpty(token)) {
System.out.println("鉴权失败。确少token参数。");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
if (!"jack".equals(token)) {
System.out.println("token无效...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 继续执行filter链
return chain.filter(exchange);
}
// 顺序,数值越小,优先级越高
@Override
public int getOrder() {
return 0;
}
}
启动测试: