Search K
Appearance
Appearance
最近在做公司业务时需要在网关微服务自定义过滤器,特此记录总结下。
参考地址:https://blog.csdn.net/Cavewang/article/details/123854065 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。涉及转载内容目的是为了学习,无商业用途。
Spring-Cloud-Gateway 基于过滤器实现,同 zuul 类似,有pre和post两种方式的 filter,分别处理前置逻辑和后置逻辑。客户端的请求先经过pre类型的 filter,然后将请求转发到具体的业务服务,收到业务服务的响应之后,再经过post类型的 filter 处理,最后返回响应到客户端。 过滤器执行流程如下,order 越大,优先级越低,如图所示。
过滤器分为全局过滤器和局部过滤器。
全局过滤器:对所有路由生效
局部过滤器:对指定的路由生效
com.atguigu.gulimall.gateway.filters.FilterConfig
package com.atguigu.gulimall.gateway.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class FilterConfig {
@Bean
public GlobalFilter a(){
return new AFilter();
}
@Bean
public GlobalFilter b(){
return new BFilter();
}
@Bean
public GlobalFilter c(){
return new CFilter();
}
@Bean
public GlobalFilter myAuthFilter(){
return new MyAuthFilter();
}
@Slf4j
static class AFilter implements GlobalFilter, Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("AFilter前置逻辑");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("AFilter后置逻辑");
}));
}
@Override
public int getOrder() {
// 值越小,优先级越高
return HIGHEST_PRECEDENCE + 100;
}
}
@Slf4j
static class BFilter implements GlobalFilter, Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("BFilter前置逻辑");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("BFilter后置逻辑");
}));
}
@Override
public int getOrder() {
// 值越小,优先级越高
return HIGHEST_PRECEDENCE + 200;
}
}
@Slf4j
static class CFilter implements GlobalFilter, Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("CFilter前置逻辑");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("CFilter后置逻辑");
}));
}
@Override
public int getOrder() {
// 值越小,优先级越高
return HIGHEST_PRECEDENCE + 300;
}
}
@Slf4j
static class MyAuthFilter implements GlobalFilter,Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("MyAuthFilter权限过滤器");
String token = exchange.getRequest().getHeaders().getFirst("auth");
if(StringUtils.isBlank(token)){
// exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// return exchange.getResponse().setComplete();
log.info("无token,权限校验失败");
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 400;
}
}
}
需要实现GatewayFilter, Ordered,实现相关的方法
包装GatewayFilter,产生GatewayFilterFactory
GatewayFilterFactory加入到过滤器工厂,并且注册到spring容器中。
在配置文件中进行配置,如果不配置则不启用此过滤器规则。
接下来定义局部过滤器,对于请求头user-id校验,如果不存在user-id请求头,直接返回状态码406。代码如下。
com.atguigu.gulimall.gateway.filters.UserIdCheckGatewayFilterFactory
package com.atguigu.gulimall.gateway.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
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;
@Component
public class UserIdCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public GatewayFilter apply(Object config) {
return new UserIdCheckGateWayFilter();
}
@Slf4j
static class UserIdCheckGateWayFilter implements GatewayFilter, Ordered{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String url = exchange.getRequest().getPath().pathWithinApplication().value();
log.info("请求URL: {}",url);
log.info("method: {}", exchange.getRequest().getMethod());
// 获取header
String userId = exchange.getRequest().getHeaders().getFirst("user-id");
log.info("userId: {}",userId);
if(StringUtils.isBlank(userId)){
log.info("头部验证不通过,请在头部输入 user-id");
//终止请求,直接回应
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
// 值越小,优先级越高
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
}
application.yml
spring:
cloud:
gateway:
routes:
# 商品服务
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
# 第三方服务
- id: third_party_route
uri: lb://gulimall-third-party
predicates:
- Path=/api/thirdparty/**
filters:
- RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}
# 后台管理服务
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
default-filters:
- PrefixPath=/api/product/**
- UserIdCheck
这里可以发现我们在配置文件中直接使用UserIdCheck
这个名字就可以自动识别到我们定义的局部过滤器,这是因为它继承自AbstractGatewayFilterFactory<Object>
。Spring Cloud Gateway会根据你的GatewayFilterFactory
的类名来生成一个过滤器引用的名字。具体来说,它会将类名中的GatewayFilterFactory
后缀去掉,并将剩下的部分转换为小写(如果存在驼峰命名),然后用这个名字来引用过滤器。UserIdCheckGatewayFilterFactory
的类名在去掉GatewayFilterFactory
后缀后,变成了UserIdCheck
,所以你可以在你的路由配置中直接使用UserIdChec
k这个名字来引用这个过滤器。
可以看到自定义的局部过滤器已经成功生效了,但是我们之前定义的全局过滤器并未生效。
这是因为我们的局部过滤器的执行顺序高于全局过滤器,那么局部过滤器可能会先执行,并可能改变请求的状态或终止请求,导致全局过滤器无法执行或执行时看不到预期的效果。而我们局部过滤器中已经终止了本次请求,所有后续的全局过滤器并未执行。