【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请求:
【Spring Cloud Gateway专题】三、自定义gatewayfilter实现AddRequestHeader_第1张图片
两次请求结果(不同时间点):

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章节

你可能感兴趣的:(API网关)