【Spring Cloud Gateway专题】三、自定义gatewayfilter实现AddRequestHeader

1、前言

实际项目中存在这样一个场景,使用Spring Cloud Gateway建设一个网关,该网关负责代理所有业务系统的对外访问请求。若外部服务需要授权(下文中的passID和passToken)才能访问,那么该网关可以统一处理该问题。
以下引出添加AddRequestHeader的几种方式,以及存在的限制。

2、使用yaml配置方式

采用yaml方式配置 ,访问csdn网站,X-Request-red = blue,该方式适用于已知数据的添加。

spring:
  application:
    name: gateway-application
  cloud:
    # Spring Cloud Gateway 配置项,对应 GatewayProperties 类
    gateway:
      # 路由配置项,对应 RouteDefinition 数组
      routes:
        - id: csdn1 # 路由的编号
          uri: https://blog.csdn.net # 路由到的目标地址,只能到域名部分 https://blog.csdn.net/dear_little_bear
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/csdn1
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Request-red, blue

3、使用RouteLocator编程方式

采用RouteLocator编程方式 ,访问csdn网站,该方式适用于不变的数据(“单例”)的添加。

@Configuration
public class RouteConfiguration {
    @Value("${gateway.csdn.host}")
    private String csdnHost;

    @Value("${gateway.csdn.PaaSID}")
    private String paaSID;

    @Value("${gateway.csdn.PaaSToken}")
    private String paaSToken;

    @Value("${gateway.csdn.nonce}")
    private String nonce;

    @Autowired
    private CsdnRequestFilter csdnRequestFilter;

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) throws UnsupportedEncodingException {
        String timeTamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signature = SecureUtil.sha256(timeTamp + nonce + paaSToken);

        return builder.routes()
                .route(r -> r.path("/csdn2/**")
                        .filters(f ->
                                f.stripPrefix(1)
                                .addRequestHeader("x-tif-nonce", nonce)
                                .addRequestHeader("x-tif-signature", signature)
                                .addRequestHeader("x-tif-paasid", paaSID)
                                .addRequestHeader("x-tif-timestamp", timeTamp))
                        .uri(csdnHost)
                        .order(2))
                /*.route(r -> r.path("/csdn3/**")
                        .filters(f->f.stripPrefix(1)
                                .filter(csdnRequestFilter))
                        .uri(csdnHost)
                        .order(3))*/
                .build();
    }
}

使用Postman请求:
在这里插入图片描述
两次请求结果(不同时间点):

2020-04-04 14:16:15.141 DEBUG 10204 — [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8888/csdn2] to Route{id=‘c3c865da-0fc2-47e3-aef2-55864afd626f’, uri=https://blog.csdn.net:443, order=2, predicate=Paths: [/csdn2/**], match trailing slash: true, gatewayFilters=[[[StripPrefix parts = 1], order = 0], [[AddRequestHeader x-tif-nonce = ‘123456789abcdefg’], order = 0], [[AddRequestHeader x-tif-signature = ‘3dee7471fe6b71a2b66f3b187c8cca844c3aef47afa4020f7df3b5c386015955’], order = 0], [[AddRequestHeader x-tif-paasid = ‘csdndemo’], order = 0], [[AddRequestHeader x-tif-timestamp = ‘1585980879’], order = 0]], metadata={}}

2020-04-04 14:20:14.343 DEBUG 10204 — [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8888/csdn2] to Route{id=‘c3c865da-0fc2-47e3-aef2-55864afd626f’, uri=https://blog.csdn.net:443, order=2, predicate=Paths: [/csdn2/**], match trailing slash: true, gatewayFilters=[[[StripPrefix parts = 1], order = 0], [[AddRequestHeader x-tif-nonce = ‘123456789abcdefg’], order = 0], [[AddRequestHeader x-tif-signature = ‘3dee7471fe6b71a2b66f3b187c8cca844c3aef47afa4020f7df3b5c386015955’], order = 0], [[AddRequestHeader x-tif-paasid = ‘csdndemo’], order = 0], [[AddRequestHeader x-tif-timestamp = ‘1585980879’], order = 0]], metadata={}}

我们发现timestamp和signature都没有发生变化,即便在customRouteLocator上加上Scope,依然无效。查看源码后发现,当我们启动了Spring Cloud Gateway之后,该RouteLocator就会建立,并且不会重复new。但这不是我们想要的结果。

4、使用自定义gatewayfilter方式

(1) 自定义一个CsdnRequestFilter实现GatewayFilter,CsdnRequestFilter 代码如下:

@Component
public class CsdnRequestFilter implements GatewayFilter, Ordered {

    @Value("${gateway.csdn.PaaSID}")
    private String paaSID;

    @Value("${gateway.csdn.PaaSToken}")
    private String paaSToken;

    @Value("${gateway.csdn.nonce}")
    private String nonce;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String timeTamp = String.valueOf(System.currentTimeMillis() / 1000);
        String signature = SecureUtil.sha256(timeTamp + nonce + paaSToken);
        System.out.print("自定义过滤器:timeTamp:"+timeTamp+";signature:"+signature);
        //提取应用账户及应用令牌,鉴权
        ServerHttpRequest request = exchange.getRequest().mutate()
                .header("x-tif-nonce", nonce)
                .header("x-tif-signature", signature)
                .header("x-tif-paasid", paaSID)
                .header("x-tif-timestamp", timeTamp)
                .build();

        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

(2) 在RouteLocator类中添加该gatewayfilter,代码如下:

        return builder.routes()
                .route(r -> r.path("/csdn3/**")
                        .filters(f->f.stripPrefix(1)
                                .filter(csdnRequestFilter))
                        .uri(csdnHost)
                        .order(3))
                .build();

两次请求结果如下:

自定义过滤器:timeTamp:1585982693;signature:110ad2c949fb9e9afe2c8f98a81f6f445fd85ad43e0dec1ab555dae8f06b8f62 2020-04-04 14:44:53.180

自定义过滤器:timeTamp:1585980894;signature:265ff5cbac42c99969403285b897c24e2db59151f456a842669c7e7726fb9c23 2020-04-04 14:14:54.504

发现timeTamp和signature都发生了改变。原因是自定义gatewayfilter会在每次http请求过程中,动态创建。
特别注意
header是readonly类型,所以在CsdnRequestFilter中新增header,采用的是exchange.getRequest().mutate(),该方法将会构造出一个新的ServerHttpRequest。同时,也使用了exchange.mutate() 方法重新构造了一个ServerWebExchange 。

以上希望对你有帮助。
源码: https://github.com/muziye2013/SpringBoot-Labs 参照 labx-08/labx-08-ex-gateway-demo01章节

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个简单的自定义负载均衡器的代码示例: 1. 创建自定义负载均衡器类 ```java import java.util.List; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.gateway.config.LoadBalancerProperties; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactoryWrapper; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class CustomLoadBalancerClientFilter extends LoadBalancerClientFilter { public CustomLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) { super(clientFactory, properties); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 自定义负载均衡逻辑 System.out.println("Custom Load Balancer executed!"); return super.filter(exchange, chain); } } ``` 2. 创建自定义负载均衡器工厂类 ```java import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Component public class CustomLoadBalancerClientFilterFactory extends AbstractGatewayFilterFactory<CustomLoadBalancerClientFilterFactory.Config> { private final LoadBalancerClientFactory clientFactory; public CustomLoadBalancerClientFilterFactory(ApplicationContext context, LoadBalancerClientFactory clientFactory) { super(Config.class); this.clientFactory = new LoadBalancerClientFactoryWrapper(clientFactory, context); } @Override public GatewayFilter apply(Config config) { return new CustomLoadBalancerClientFilter(clientFactory, getLoadBalancerProperties()); } public static class Config { // 可以在这里添加自定义配置项 } } ``` 3. 配置自定义负载均衡器 在Spring Boot应用程序中的配置文件(如application.yml)中添加以下配置: ```yaml spring: cloud: gateway: routes: - id: my_route uri: lb://my_service predicates: - Path=/foo/** filters: - CustomLoadBalancerClient ``` 或者在Java代码中使用以下方式配置: ```java @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("my_route", r -> r.path("/foo/**") .filters(f -> f.filter(new CustomLoadBalancerClientFilterFactory().apply(new CustomLoadBalancerClientFilterFactory.Config()))) .uri("lb://my_service")) .build(); } ``` 以上代码示例中,我们创建了一个名为“CustomLoadBalancerClientFilter”的自定义负载均衡器,该负载均衡器在执行时会打印一条日志。我们还创建了一个名为“CustomLoadBalancerClientFilterFactory”的自定义负载均衡器工厂类,这个工厂类会返回一个自定义负载均衡器实例。最后,我们在Spring Cloud Gateway的配置中使用了这个自定义负载均衡器。 希望这个示例对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值