IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringCloud Alibaba | 网关(三) : SpringCloudGateway 过滤器获取application/json中body数据 -> 正文阅读

[Java知识库]SpringCloud Alibaba | 网关(三) : SpringCloudGateway 过滤器获取application/json中body数据

一、前言

???项目接口需要加解密,就在网关层进行解密操作。那么问题来了怎么在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方法。

四、(* ̄︶ ̄)

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

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

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 10:52:38  更:2022-12-25 10:54:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 6:20:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码