微服务学习之网关(Gateway)断言和过滤器

微服务系列

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剔除响应头中重复的数据
CircuitBreakerGateway 断路器
FallbackHeaders为fallbackUri的请求头中添加具体执行异常信息
PrefixPath为请求路径添加前缀

可以参考Spring Cloud 中文文档

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);
    }

}

Spring Cloud 中文文档

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值