SpringCloud Alibaba | 网关(三) : SpringCloudGateway 过滤器获取application/json中body数据

news2024/11/16 8:19:52

SpringCloudGateway 过滤器获取application/json中body数据

  • 一、前言
  • 二、通过cachedRequestBodyObject缓存获取
  • 三、ServerHttpRequest getBody方法获取
  • 四、(* ̄︶ ̄)

一、前言

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

四、(* ̄︶ ̄)

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/108044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于松鼠算法改进的DELM预测-附代码

松鼠算法改进的深度极限学习机DELM的回归预测 文章目录松鼠算法改进的深度极限学习机DELM的回归预测1.ELM原理2.深度极限学习机&#xff08;DELM&#xff09;原理3.松鼠算法4.松鼠算法改进DELM5.实验结果6.参考文献7.Matlab代码1.ELM原理 ELM基础原理请参考&#xff1a;https:…

线程池相关

文章目录为什么需要线程池&#xff1f;池化思想常用方法execute()方法submit()方法shutdownisShutdownisTerminatedawaitTerminationshutdownNow创建线程池 七个参数流程JAVA线程池有哪几种类型?线程池常用的阻塞队列有哪些?源码中线程池是怎么复用线程的?如何合理配置线程池…

EMQX Cloud 自定义函数实现多种 IoT 数据形式的灵活转化

物联网场景中&#xff0c;各类设备终端的种类繁杂&#xff0c;所使用的通信协议各异&#xff0c;从而使得应用层的数据格式也各不相同。为了帮助用户实现统一数据格式&#xff0c;EMQX Cloud 最近推出了自定义函数功能&#xff1a;根据用户自定义的脚本对设备上报的数据进行预处…

上美股份在港交所上市:预计全年利润下滑,一叶子收入持续走低

12月22日&#xff0c;上海上美化妆品股份有限公司&#xff08;HK:02145&#xff0c;下称“上美股份”&#xff09;在港交所上市。本次上市&#xff0c;上美股份的发行价格为25.20港元/股&#xff0c;为此前发行区间的最低值。据此计算&#xff0c;上美股份的募资总额约为9.31亿…

CDH6.3.2集成Apache Atlas2.1.0

1 环境准备 1.1 CDH6.3.2 环境搭建 参考文档如下 Cloudera Manager安装CDH6教程-&#xff08;一&#xff09;虚拟环境安装配置 Cloudera Manager安装CDH6教程-&#xff08;二&#xff09;搭建Cloudera和CDH6 CM和CDH在安装的时候遇到的问题 CDH6.3.2 各组件版本 1.2 apa…

火爆“有机新消费”驶入酱油赛道 好记打造我国有机酱油行业领导品牌

根据观研报告网发布的《2022年中国有机酱油市场分析报告-市场竞争策略与发展动向前瞻》显示&#xff0c;有机酱油是指采用有机农作物为原料酿制的酱油。有机酱油含有浓郁的酱香和脂香&#xff0c;是一种不可多得的上等调味品&#xff0c;适合于蘸食&#xff0c;红烧&#xff0c…

以技术创新践行社会责任,欧科云链斩获界面新闻年度双项大奖

12月20日&#xff0c;欧科云链凭借在区块链技术领域的创新&#xff0c;与腾讯、宁德时代和埃森哲等各领域领先企业一同荣获“2022好公司行业领先大奖”。 12月21日&#xff0c;欧科云链以区块链科技之力积极践行社会责任&#xff0c;绽放商业向善的力量&#xff0c;与茅台、蚂蚁…

华为云Stack智能进化,三大举措赋能政企深度用云

【中国&#xff0c;深圳&#xff0c;2022年12月22日】今天&#xff0c;以“政企深度用云&#xff0c;释放数字生产力”为主题的华为云Stack战略暨新品发布会在线上举办&#xff0c;华为云围绕“让技术不难用、让场景变简单、让经验可复制”提出三大关键举措赋能政企迈向深度用云…

无线鼠标怎么连接电脑?2个方法,轻松学会

现在越来越多的小伙伴都喜欢使用无线鼠标。相比于有线鼠标&#xff0c;无线鼠标方便快捷&#xff0c;不会受到USB线的束缚。可是很多小伙伴买回无线鼠标&#xff0c;第一次使用的时候&#xff0c;却发现自己不知道怎么使用。无线鼠标怎么连接电脑&#xff1f;今天小编给大家带来…

对于转行Pyhon程序员,这些是我们需要知道的事情..

不知道从什么时候开始&#xff0c;我们焦虑当下&#xff0c;担心未来&#xff0c;踌躇和迷茫甚至痛苦成为我们的“生活伴侣”。 我无法定义这是好是坏&#xff0c;但我知道这肯定很难受。于是乎总很多人想寻得一个答案&#xff0c;希望可以抹平心中的波澜。而往往事与愿违。 …

口罩后,那些被“优化”的程序员都去哪儿了?

程序员在35岁真的会被裁吗&#xff1f; 被裁之后去哪儿工作 &#xff0c;怎么办啊&#xff1f; 在很多社交平台&#xff0c;经常能看到不少小伙伴问出这样的问题&#xff0c;既迷茫&#xff0c;又慌张。有人说自己25岁就已经被裁了&#xff0c;也有人说做程序员&#xff0c;3…

江苏移动MGV3000-YS(S)/YS(M)-S905L3卡刷和线刷固件包

江苏移动MGV3000-YS(S)&#xff0f;YS(M)-S905L3卡刷和线刷固件包 固件特点&#xff1a; 1、修改dns&#xff0c;三网通用&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、无开机广告&#xff0c;无系统更新&#xff0c;不在被强制升级&#xff1b…

数据结构与算法java实战篇--链表

目录 一.链结点 二.单链表 1.单链表的插入方法insertFirst() 2.单链表的删除方法deleteFirst() 3.链表显示displayList() 4.单链表代码&#xff1a; 三. 查找和删除指定链结点 四.双端链表 五.用链表实现的栈 六.用链表实现队列 七.有序链表 八.双向链表 1.遍历 2.插入…

“量子深度学习的春天是否已来?” | CNCC论坛分享

2022年12月8日举办的CNCC2022“量子深度学习的春天是否已来&#xff1f;”技术论坛&#xff0c;从学术、技术和产业的角度出发&#xff0c;深入探讨未来5到10年深度学习发展在算力方面的瓶颈问题、量子深度学习会给AI领域带来何种演变以及量子深度学习的技术落地是否依然久远等…

代码随想录Day58|739. 每日温度、496.下一个更大元素 I

文章目录739.每日温度496.下一个更大元素 I739.每日温度 文章讲解&#xff1a;代码随想录 (programmercarl.com) 题目链接&#xff1a;739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 请根据每日 气温 列表&#xff0c;重新生成一个列表。对应位置…

weidl x DeepRec:热门微博推荐框架性能提升实战

微博推荐团队&#xff1a;陈雨、韩楠、蔡小娟、高家华 1.项目背景 热门微博是新浪微博的重要功能之一&#xff0c;包含热门流、热点流、频道流、小视频后推荐、视频社区等场景。 标推荐首页 发现页推荐 沉浸视频题weidl机器学习框架为热门微博在线学习提供模型训练和推理服务…

【问题记录与解决】ModuleNotFoundError: No module named ‘pymongo‘ 并测试代码

目录 一、问题记录二、解决方法一(不一定好使)三、法二(亲测可以使用)四、安装 pymongo 的目的一、问题记录 报错内容是缺少对应的模块, ModuleNotFoundError: No module named pymongo… 二、解决方法一(不一定好使) 直接在PyCharm中下载 pymongo模块即可。 但是这…

试卷安全分发系统

摘要 高校教务管理过程中&#xff0c;试卷以明文形式传输和集中存储&#xff0c;存在数据泄漏安全隐患。现提出了一个基于数字证书的试卷防泄漏方案&#xff0c;采用算法加密试卷&#xff0c;试卷在传输过程中中以密文的形式传输&#xff0c;每次传输的时候都会对试卷进行签名…

CadQuery二维Sketch教程

2D草图是3D CAD的基础。本文介绍如何在CadQuery中使用不同的方法构建二维草图&#xff08;Sketch&#xff09;。 1、基于面的 API 构建2D草图的主要方法是基于构建面并使用布尔运算将它们组合起来。 import cadquery as cqresult (cq.Sketch().trapezoid(4,3,90).vertices…

【GO】 K8s 管理系统项目[API部分--StatefulSet]

K8s 管理系统项目[API部分–StatefulSet] 1. 接口实现 service/dataselector.go // statefulSetCell type statefulSetCell appsv1.StatefulSetfunc(s statefulSetCell) GetCreation() time.Time {return s.CreationTimestamp.Time }func(s statefulSetCell) GetName() stri…