微服务系列
1、Nacus 服务搭建及使用
2、Nacos 配置中心
3、Nacos 服务注册与发现之OpenFeign服务间调用
4、Spring Security & Oauth2 认证授权
5、网关(Gateway)的搭建及使用
6、网关(Gateway自定义断言和过滤器)
文章目录
前言
在微服务学习之网关(Gateway)的搭建及使用中介绍了Gateway内置的断言处理和过滤器,但是并不能满足我们负责的业务逻辑使用,所以我们需要自定义 Prediate 和 Filter。
一、自定义断言(Predicate)
假设有一个场景是用户名必须是大于 5 个字符才可以通过进行注册,那就用自定义断言来实现。
1、定义 PredicateConfig 类
定义 PredicateConfig 类,作为自定义断言工厂类测泛型实例封装 配置类中的 args 参数。
public class PredicateConfig {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2、创建自定义断言工厂
自定义断言工厂类(CheckUserRoutePredicateFactory)需要继承 抽象工厂类 AbstractRoutePredicateFactory,并且要变成一个 Bean 交给 Spring 管理,所以需要加上注解 @Component 让其能够扫描成一个 Bean。
注意:的就是 自定义工厂类 需要以 RoutePredicateFactory 结尾命名,因为 Spring 要通过类名进行反射实例化,在配置的时候只需要配置类名前半部分即可。
@Component
public class CheckUserRoutePredicateFactory extends AbstractRoutePredicateFactory<PredicateConfig> {
private final Logger logger = LoggerFactory.getLogger(CheckUserRoutePredicateFactory.class);
public CheckUserRoutePredicateFactory() {
super(PredicateConfig.class);
}
}
3、配置自定义断言(application.yml)
创建好自定义工厂类,按部就班,需要告诉 Gateway 框架,让其起作用,所以需要在配置文件中配置(按理说配置类应该也行的,回头可以可以试试)。
下面的配置,spring.cloud.gateway.routes.predicates.name 的值是 CheckUser,就是自定义配置工厂类的前半部分,Gateway 会把 username 赋值给 PredicateConfig 中的 name 属性。
server:
port: 8088 #服务端
spring:
application:
name: gateway-server
#配置gateway
cloud:
gateway:
#路由规则
routes:
- id: home-server # 路由的唯一标识
uri: http://localhost:8001
predicates:
- Path=/home-server/**
- name: CheckUser
- args:
name: username
filters:
- StripPrefix=1 #替换调 home-server实现跳转
3、重写 apply 方法校验用户信息
需要重写 apply 方法 和 shortcutFieldOrder 方法,实现业务需求,apply 方法是具体的业务逻辑。
完整代码如下:
@Component
public class CheckUserRoutePredicateFactory extends AbstractRoutePredicateFactory<PredicateConfig> {
private final Logger logger = LoggerFactory.getLogger(CheckUserRoutePredicateFactory.class);
public CheckUserRoutePredicateFactory() {
super(PredicateConfig.class);
}
/**
* 重新 apply 方法
* @param config
* @return
*/
@Override
public Predicate<ServerWebExchange> apply(PredicateConfig config) {
return (GatewayPredicate) serverWebExchange -> {
MultiValueMap<String, String> queryParams = serverWebExchange.getRequest().getQueryParams();
if(queryParams.containsKey(config.getName())){
String username = queryParams.getFirst(config.getName());
if(!StringUtils.isEmpty(username)){
logger.info("检查用户信息是否合法 {}", username);
if(username.length() > 5){
return true;
}
}
}
return false;
};
}
}
4、调用测试
用 postman 调用测试,如果 username=Yphen,因为在 apply 方法中,username 参数值 必须大于 5 才通过,所以调用接口返回了 404。
但是如果username=Yphen123则就可以通过了。
二、过滤器(Filter)
Spring Cloud Gateway内置了很多过滤器工厂,我们可以通过过滤器工厂进行一些业务逻辑处理,比如:添加删除请求头,请求参数之类的。
2.1、Gateway内置过滤器
AddRequestHeader | 添加请求头参数 |
---|---|
AddRequestParameter | 添加请求参数 |
AddResponseHeader | 添加响应头 |
DedupeResponseHeader | 剔除响应头中重复的数据 |
CircuitBreaker | Gateway 断路器 |
FallbackHeaders | 为fallbackUri的请求头中添加具体执行异常信息 |
PrefixPath | 为请求路径添加前缀 |
2.2、自定义过滤器
2.2.1、创建自定义过滤器类
自定义过滤器类 CheckUserInfoGatewayFilterFactory 继承 AbstractNameValueGatewayFilterFactory 并且重新 apply 方法。
如果username参数匹配失败,则不往下执行。
@Component
public class CheckUserInfoGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
private final Logger logger = LoggerFactory.getLogger(CheckUserInfoGatewayFilterFactory.class);
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
logger.info("检查用户信息 Filter {},{}", config.getName(), config.getValue());
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
if(Objects.nonNull(queryParams) && queryParams.size() > 0){
if(queryParams.get(config.getName()).size() == 0){
return null;
}
}
return chain.filter(exchange);
};
}
}
2.2.2、配置 application.yml
CheckUserInfo=username,Yphen ,CheckUserInfo是过滤器名称前部分,username是参数名,Yphen是参数值。
server:
port: 8088 #服务端
spring:
application:
name: gateway-server
#配置gateway
cloud:
gateway:
#路由规则
routes:
- id: home-server # 路由的唯一标识
uri: http://localhost:8001
predicates:
- Path=/home-server/**
- name: CheckUser
args:
name: username
filters:
- StripPrefix=1 #替换调 home-server实现跳转
- CheckUserInfo=username,Yphen
2.3、全局过滤器
GlobalFilter 全局过滤器,不需要进行配置就可以作用在所有的路由上,通过 GatewayFilterAdapeter 包装成 GatewayFilterChain 能够识别的过滤器。
过滤器类型 | 过滤器 | 作用 |
---|---|---|
负载均衡过滤器 | LoadBalancerClinentFilter | 通过负载均衡过滤器,把路径的URL解析转换成真实的请求URL并均衡的调用每个服务 |
Http客户端过滤器 | NettyRoutingFilter, NettyWriteResponseFilter | 通过 HttpClient客户端转发请求真实的URL并将响应写入到当前的请求响应中 |
WebSocket 相关过滤器 | WebSocketRoutingFilter | 专门处理 WebSocket 类型的请求响应信息 |
路径转发相关过滤器 | ForwardPathFilter | 解析路径并且将路径转发 |
路由相关过滤器 | RouteToRequestUrlFilter | 转换路由中的URI |
WebClient 相关过滤器 | WebClientHttpRoutingFilter,WebClientWriteResponseFilter | 通过WebClient客户端转发请求真实的URL并将响应结果写入当前的请求响应中 |
2.3.1 LoadBalancerClientFilter(负载均衡过滤器 )
只需要通过下面的配置就可以使用:
关键是 lb,负责均衡过滤器会检测到,如果是 lb 的话进行负载调用。
spring:
cloud:
gateway: #配置gateway
routes:
- id: home-server
uri: lb://home-server
predicates:
- Path=/home/**
2.3.2 CorsWebFilter(跨域配置)
spring:
cloud:
gateway: #配置gateway
routes:
- id: home-server
uri: lb://home-server
predicates:
- Path=/home/**
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- OPTION
通过 Configuration 配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
2.4、自定义全局过滤器
新建 CheckAuthFilter全局过滤器,实现 GlobalFilter 并重新 filter 方法即可。
@Component
@Order(-1)
public class CheckAuthFilter implements GlobalFilter {
private final Logger logger = LoggerFactory.getLogger(CheckAuthFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("校验全局用户权限");
List<String> token = exchange.getRequest().getHeaders().get("token");
if(token.isEmpty()){
return null;
}
return chain.filter(exchange);
}
}