一、前言

   项目接口需要加解密,就在网关层进行解密操作。那么问题来了怎么在gateway 的filter 中获取 body(application/json)中的数据呢? 经过一顿百度一顿验证发现了两种方式

  • 一种是通过cachedRequestBodyObject缓存获取request body信息
  • 一种是通过ServerHttpRequest.getBody方法获取body信息

二、通过cachedRequestBodyObject缓存获取

通过cachedRequestBodyObject 获取的话 路由配置需要使用java方式配置

ReadBodyPredicateFactory里面缓存了request body的信息,于是在自定义router中配置了ReadBodyPredicateFactory,然后在filter中通过cachedRequestBodyObject缓存字段获取request body信息。

路由配置

/**
 * 路由配置
 *
 */
@EnableAutoConfiguration
@Configuration
public class GatewayRouteConfig {

    @Resource
    private RequestParamDecryptFilter2 paramDecryptFilter;

    private static final String PATH_URL = "/UserCenter/**";


    @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {

        return builder.routes()
                //拦截请求类型为POST Content-Type application/json application/json;charset=UTF-8
                .route(r -> r
                        .header(HttpHeaders.CONTENT_TYPE,
                                MediaType.APPLICATION_JSON_VALUE + MediaType.APPLICATION_JSON_UTF8_VALUE)
                        .and()
                        .method(HttpMethod.POST)
                        .and()
                        //TODO 这里是核心,获取缓存中的请求体
                        .readBody(Object.class, readBody -> {
                            return true;
                        })
                        .and()
                        .path(PATH_URL)
                        //把请求体传递给拦截器reqTraceFilter
                        .filters(f -> {
                            f.filter(paramDecryptFilter);
                            return f;
                        })
                        //这里换成 nacos 的配置地址
                        .uri("http://127.0.0.1:8083")).build();
    }
}

自定义filter

/**
 * 解密操作
 *
 * @description: 过滤器,
 * @modified:
 */
@Slf4j
@Component
public class RequestParamDecryptFilter2 implements GatewayFilter, Ordered {


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


    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        
        Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
        if (null != cachedBody) {
           String bodyStr = cachedBody.toString();
           
           //TODO 这里进行 业务逻辑操作
        }
        return chain.filter(exchange);
    }

  
}

三、ServerHttpRequest getBody方法获取

通过gateBody()方法获取 需要两个filter

  • 第一个filter 进行ServerHttpRequest getBody方法的重写
  • 第二个filter 解析body

ServerHttpRequest getBody 重写filter

/**
 * ServerHttpRequest getBody 包装Filter
 *
 * @description: 过滤器,
 * @modified:
 */
@Slf4j
@Component
public class ServerHttpReqFilter implements GlobalFilter, Ordered {

    /**
     * 设置最高优先级 保证在 获取body 之前执行
     *
     * @return
     */
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

          return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            DataBufferUtils.release(dataBuffer);
            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                DataBufferUtils.retain(buffer);
                return Mono.just(buffer);
            });

            /**
             * repackage ServerHttpRequest
             */
            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return cachedFlux;
                }
            };
            /**
             * mutate exchage with new ServerHttpRequest
             */
            ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
            /**
             * read body string with default messageReaders
             */
            return ServerRequest.create(mutatedExchange, HandlerStrategies.withDefaults().messageReaders()).bodyToMono(String.class)
                    .doOnNext(objectValue -> {
                    }).then(chain.filter(mutatedExchange));
        });
    }
}

真正获取body 数据进行 业务逻辑处理的filter


/**
 * 解密操作
 * 
 * @description: 过滤器,
 * @modified:
 */
@Slf4j
@Component
public class RequestParamDecryptFilter implements GlobalFilter, Ordered {


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

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        log.info("request path :{}", request.getPath());
        //TODO 进行Content-type 与 method 判断
        
        StringBuffer paramBuffer = new StringBuffer();
        Flux<DataBuffer> body = exchange.getRequest().getBody();
        body.subscribe(buffer -> {
            //解析body 数据
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            log.info("request  charBuffer:{}", charBuffer);
            //在这里解密 并 重新塞进去
            paramBuffer.append(charBuffer);
        });
        //获取到最后的body 中的数据
        String param = paramBuffer.toString();
        log.info("request param :{}", paramBuffer.toString());
        //TODO 这里进行业务操作
        //......

        //重新封装参数 向下传递
        DataBuffer bodyDataBuffer = stringBuffer(param);
        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
        request = new ServerHttpRequestDecorator(request) {
            @Override
            public Flux<DataBuffer> getBody() {
                return bodyFlux;
            }
        };//封装我们的request
        return chain.filter(exchange.mutate().request(request).build());
    }


    protected DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);

        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }
}

那么为什么要用两个filter?

 StringBuffer paramBuffer = new StringBuffer();
        Flux<DataBuffer> body = exchange.getRequest().getBody();
        body.subscribe(buffer -> {
            //解析body 数据
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            log.info("request  charBuffer:{}", charBuffer);
            //在这里解密 并 重新塞进去
            paramBuffer.append(charBuffer);
        });
        //获取到最后的body 中的数据
        String param = paramBuffer.toString();

      在以上代码中,当你直接通过body.subscribe 解析数据的时候,你会发现他在filter执行完才会执行body.subscribe()方法内的内容(debug可以试试 在filter执行完才会debug到里边)。
      SpringCloudGateWay 是底层是 Spring webflux 是非阻塞线程的,所以当你直接通过body.subscribe() 来解析的话 他还没执行 你就直接用 paramBuffer 来 这个时候获取的是null 所以需要重写 getBody方法。

四、(* ̄︶ ̄)

如果还有其他好的获取方法, 请评论告知一下,共同学习,共同进步。谢谢啦

如果对你有帮助,加个关注把~
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐