解决HttpServletRequest中的InputStream/getReader只能被读取一次的问题

一、事由

由于我们业务接口需要做签名校验,但因为是老系统了签名规则被放在了Body里而不是Header里面,但是我们不能在每个Controller层都手动去做签名校验,这样不是优雅的做法,然后我就写了一个AOP,在AOP中实现签名校验,之后Controller层就报以下错误:

java.lang.IllegalStateException: getReader() has already been called for this requestat org.apache.catalina.connector.Request.getInputStream(Request.java:1074)at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:365)at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:151)at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:151)at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:151)

二、分析源码

它告诉我们,getReader方法在这次请求中已经被调用了,可是为什么getReader已经被调用了呢,我们来分析一下源码,只有看懂了源码我们才能理解为什么,同时也为自己积累源码Debug的经验,这是我的Controller层代码(这里只是做了个实例读取流数据):

 @RequestMapping("/2")public String index2(HttpServletRequest request) throws IOException {return "请使用前端页面进行访问 " + request.getInputStream().read();}

判断条件
但是usingReader为什么会为true呢,我们点击看一下这个变量赋值的地方:

它一共有两个赋值的地方,第一个赋值为fase我们可以直接忽略,看一下true那个
在这里插入图片描述
在这里插入图片描述

三、解决办法

我看了这个getReader方法调用地方就是我在AOP中读取流中数据做签名校验的时候取的,那这咋办?AOP做了一层数据读取就导致Controller拿不到数据了,想了又想,我想到一个解决办法:

使用HttpServletRequest的setAttribute方法。

这个方法用于在HTTP请求直接共享数据,并且在请求完成之后自动销毁,完全不用担心生命周期问题,用它再适合不过了,我们可以写个请求过滤器,过滤所有的请求,将body数据设置到请求域当中:

/*** 用于处理读取多次请求体内容* 读取Token一次,解析数据一次* * Created By XuanRan*/
@Component
@Order(Ordered.LOWEST_PRECEDENCE)
public class MultipleReadHttpRequestFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {StringBuilder sb = new StringBuilder();BufferedReader reader = request.getReader();String line;while ((line = reader.readLine()) != null) {sb.append(line);}request.setAttribute("CACHED_REQUEST_BODY", sb.toString());filterChain.doFilter(request, response);}
}

然后我这里使用的是SpringSecurity,我们把它添加到Filter链当中

@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 注解标记允许匿名访问的urlExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());// 中间省略了一部分.....// 中间省略了一部分.....// 中间省略了一部分.....// 中间省略了一部分.....httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);// 添加请求体读取httpSecurity.addFilterAfter(multipleReadHttpRequestFilter, CorsFilter.class);}

然后我们就可以在Controller的HttpServletRequest的getAttribute根据CACHED_REQUEST_BODY这个key取对应的数据了,我这里为了方便,进一步写了一个AOP,自动的将数据完成转换:
在这里插入图片描述

然后就可以在Controller层直接拿到处理后的对象了
在这里插入图片描述

版权所有:XuanRan
未经书面授权,禁止转载

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

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

相关文章

Stable Diffusion教程:额外功能/后期处理/高清化

"额外功能"对应的英文单词是Extras&#xff0c;算是直译。但是部分版本中的翻译是“后期处理”或者“高清化”&#xff0c;这都是意译&#xff0c;因为它的主要功能是放大图片、去噪、修脸等对图片的后期处理。注意这里边对图片的处理不是 Stable Diffusion 本身的能…

PyTorch深度学习实战(41)——循环神经网络与长短期记忆网络

PyTorch深度学习实战&#xff08;41&#xff09;——循环神经网络与长短期记忆网络 0. 前言1. 循环神经网络1.1 传统文本处理方法的局限性1.2 RNN 架构2.3 RNN 内存机制 2. RNN 的局限性3. 长短期记忆网络3.1 LSTM 架构3.2 构建 LSTM 小结系列链接 0. 前言 循环神经网络 (Recu…

传统过程自动化工厂的智能扩展

一 通过NOA概念&#xff0c;公开、安全地迈向未来 随着数字化转型在过程自动化工业中的不断深入&#xff0c;许多公司都面临着同一挑战——如何平衡创新和传统。放眼望去&#xff0c;过程自动化工业和信息技术似乎在以不同的速度发展。虽然过程自动化工厂通过使用传统的自动化…

基于Springboot的幼儿园管理系统

基于SpringbootVue的幼儿园管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 用户管理 教师管理 幼儿园信息管理 班级信息管理 工作日志管理 会议记录管理…

Vue2基础知识:组件的样式冲突scoped,为什么加了scoped样式就会独立出来呢?

默认情况&#xff1a;写在组件中的样式会全局生效&#xff0c;这样就容易造成多个组件之间的样式冲突问题。 1.全局样式&#xff1a;默认组件中的样式会作用到全局.&#xff08;也就是说不管你在哪个页面或者组件中写入样式&#xff0c;只要页面生效&#xff0c;该页面的style…

自动式挂机游戏,全新玩法,实现睡后收入,日产8000

项目简介&#xff1a; 最近&#xff0c;许多朋友询问关于支付宝钱包无人直播游戏的玩法。我已经研究了一个月&#xff0c;今天终于迎来了好消息&#xff01;市场上的价格在1980到5980元不等&#xff0c;但今天我花了1980元买下了这个游戏的玩法。老实说&#xff0c;原本这个玩法…

全志ARM-超声波测距

超声波测距模块是用来测量距离的一种产品&#xff0c;通过发送和收超声波&#xff0c;利用时间差和声音传播速度&#xff0c; 计算出模块到前方障碍物的距离 1.测距原理&#xff1a; 给Trig端口至少10us的高电平发送声波&#xff0c;Echo信号&#xff0c;由低电平跳转到高电平…

Open-Sora 升级技术报告解读

最新功能概览 开源地址&#xff1a;https://github.com/hpcaitech/Open-Sora 技术报告&#xff1a;Open-Sora/docs/report_02.md at main hpcaitech/Open-Sora GitHub技术报告&#xff1a; 支持长视频生成&#xff1b;视频生成分辨率最高可达 720p&#xff1b;单模型支持任…

vue如何发送请求给后端(包括前后端跨域)

目录 有哪些方法可以发送请求要请求先解决跨域问题代理服务器后端解决跨域问题 axios发送请求vue-resource发送请求 有哪些方法可以发送请求 以前可能了解过&#xff1a; xhr 即&#xff1a;new XMLHttpRequest()jQuery 即&#xff1a;$.get $.postaxios fetch 在vue中特有的…

数据安全能力成熟度模型(DSMM)

DSMM的架构由以下三个维度构成&#xff1a; a) 安全能力维度 安全能力维度明确了组织在数据安全领域应具备的能力&#xff0c;包括组织建设、制度流程、技术工具和人员能力。 b&#xff09;能力成熟度等级维度 数据安全能力成熟度等级划分为五级&#xff0c;具体包括&…

笔试强训未见过题(个人向)

1.游游的水果大礼包 题目 解析 我们设卖出价值为c&#xff0c;卖出x个一号礼包&#xff0c;y个二号礼包。则caxby&#xff0c;那么就可以一一枚举&#xff0c;x的最大值为min&#xff08;a/2&#xff0c;b&#xff09;&#xff0c;则y就为min&#xff08;a-2*min&#xff0c;…

《C++的类型转换》

目录 一、c语言中的类型转换 1、隐式类型转化&#xff1a; 2、强制类型转化&#xff1a; 3、缺点 二、c新的类型转换 1、内置类型转为自定义类型 3、自定义类型转换为内置类型 三、C的规范的强制类型转换 1、C新增四种规范的类型转换的原因 2、static_cast 3、reint…

PotatoPie 4.0 实验教程(29) —— FPGA实现摄像头图像均值滤波处理

图像的均值滤波简介 图像均值滤波处理是一种常见的图像处理技术&#xff0c;用于降低图像中噪声的影响并平滑图像。该方法通过在图像中滑动一个固定大小的窗口&#xff08;通常是一个正方形或矩形&#xff09;&#xff0c;将窗口中所有像素的值取平均来计算窗口中心像素的新值…

【Android】SharedPreferences阻塞问题深度分析

前言 Android中SharedPreferences已经广为诟病&#xff0c;它虽然是Android SDK中自带的数据存储API&#xff0c;但是因为存在设计上的缺陷&#xff0c;在处理大量数据时很容易导致UI线程阻塞或者ANR&#xff0c;Android官方最终在Jetpack库中提供了DataStore解决方案&#xf…

数据结构——插入排序

基本思想&#xff1a; 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 实际中我们玩扑克牌时&…

1146. 快照数组

java版本 class SnapshotArray {int id 0;List<int[]>[] snapshots;public SnapshotArray(int length) {snapshots new List[length];for (int i 0; i < length; i) {snapshots[i] new ArrayList<int[]>();}}public void set(int index, int val) {snapsho…

XYCTF-部分web总结

这个月在XYCTF中写了部分web题&#xff0c;题中学到在此记录一下 ezhttp 打开就是一个简单的登录页面 f12说藏在一个地方&#xff0c;先想到的就是robots.txt 访问直接给账号密码 username: XYCTF password: JOILha!wuigqi123$登录后: 明显考源跳转&#xff0c;修改referer值…

如何查看 UUID 是那个版本

UUID 是有多个版本的&#xff0c;如何查看 UUID 是那个版本&#xff0c;可以用 UUID 对象中的 version() 方法。 创建 UUID 对象&#xff1a; UID originalUUID UUID.fromString("cc5f93f7-8cf1-4a51-83c6-e740313a0c6c"); uuid.version();上面方法显示 UUID 的版本…

通用模型Medprompt如何在医学领域超越专家系统

在AI的发展历程中&#xff0c;一直存在着两种理念的较量&#xff1a;一种是追求普适性的通用AI模型&#xff0c;另一种是针对特定领域深度优化的专业AI系统。最近&#xff0c;微软的研究团队在这一辩论中投下了一枚重磅炸弹——他们开发的Medprompt策略&#xff0c;使得通用AI模…