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

news2024/11/18 17:28:41

一、事由

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

java.lang.IllegalStateException: getReader() has already been called for this request
	at 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 {
    @Override
    protected 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链当中

@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // 注解标记允许匿名访问的url
        ExpressionUrlAuthorizationConfigurer<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.coloradmin.cn/o/1629702.html

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

相关文章

Cesium.js(1):Cesium.js简介

1 前言 现有的gis开发方向较流行的是webgis开发&#xff0c;其中Cesium是一款开源的WebGIS库&#xff0c;主要用于实时地球和空间数据的可视化和分析。它提供了丰富的地图显示和数据可视化功能&#xff0c;并能实现三维可视化开发。Cesium适用于地球科学研究、军事情报分析、航…

Java编程练习之final关键字

1.final类&#xff1a;不允许任何类继承&#xff0c;并且不允许其他人对这个类进行任何改动&#xff1b; 当被某个类设置为final类时&#xff0c;类中的所有方法都被隐式的设置为final形式&#xff0c;但是final类中的成员变量既可以被定义为final形式&#xff0c;又可以被定义…

【区块链】椭圆曲线数字签名算法(ECDSA)

本文主要参考&#xff1a; 一文读懂ECDSA算法如何保护数据 椭圆曲线数字签名算法 1. ECDSA算法简介 ECDSA 是 Elliptic Curve Digital Signature Algorithm 的简称&#xff0c;主要用于对数据&#xff08;比如一个文件&#xff09;创建数字签名&#xff0c;以便于你在不破坏它…

Maven的仓库、周期和插件

优质博文&#xff1a;IT-BLOG-CN 一、简介 随着各公司的Java项目入库方式由老的Ant改为Maven后&#xff0c;相信大家对Maven已经有了个基本的熟悉。但是在实际的使用、入库过程中&#xff0c;笔者发现挺多人对Maven的一些基本知识还缺乏了解&#xff0c;因此在此处跟大家简单地…

SpringCloud系列(19)--将服务消费者Consumer注册进Consul

前言&#xff1a;在上一章节中我们把服务提供者Provider注册进了Consul&#xff0c;而本章节则是关于如何将服务消费者Consumer注册进Consul 1、再次创建一个服务提供者模块&#xff0c;命名为consumerconsul-order80 (1)在父工程下新建模块 (2)选择模块的项目类型为Maven并选…

使用CubeMx配置GD32F303系列单片机进行DMA ADC

原理图查看 查原理图可以看到GD32F103C8T6的官方开发板GD32303C-START-V1.0的PA1没有接任何东西 使用PA1作为ADC端口 CubeMX配置ADC和时钟 配置ADC通道 启用循环模式 配置此通道ADC分频 配置ADC DMA为循环模式 配置时钟 生成项目 Keil里面的配置 选择对应的GD32型号 编译…

2024全新瀚海跑道:矢量图片迅速养号游戏玩法,每天一小时,日转现200

最初我注意到这种玩法&#xff0c;是因为最近在浏览各大平台的视频时&#xff0c;我发现了一种特殊类型的账号&#xff0c;其养号成功率高达90%。这些账号发布的视频内容和数据非常夸张&#xff0c;而且制作起来非常简单&#xff0c;任何人都可以轻松上手。这些账号主要发布矢量…

堆与优先队列——练习题

1. 数据流中的第 K 大元素 代码实现&#xff1a; 思路&#xff1a;创建一个大小为 k 的小顶堆&#xff0c;堆顶元素就是第 K 大元素 typedef struct {int *__data, *data;int size;int n; } KthLargest;#define swap(a, b) { \__typeof(a) __c (a); \(a) (b); \(b) __c; \ }…

C++ 笔试练习笔记【1】:字符串中找出连续最长的数字串 OR59

文章目录 OR59 字符串中找出连续最长的数字串题目思路分析实现代码 注&#xff1a;本次练习题目出自牛客网 OR59 字符串中找出连续最长的数字串 题目思路分析 首先想到的是用双指针模拟&#xff0c;进行检索比较输出 以示例1为例&#xff1a; 1.首先i遍历str直到遍历到数字&a…

字符串类型漏洞之updatexml函数盲注

UPDATEXML 是 MySQL 数据库中的一个函数&#xff0c;它用于对 XML 文档数据进行修改和查询。然而&#xff0c;当它被不当地使用或与恶意输入结合时&#xff0c;它可能成为 SQL 注入攻击的一部分&#xff0c;从而暴露敏感信息或导致其他安全漏洞。 在 SQL 注入攻击中&#xff0…

CentOS 9 (stream) 安装 nginx

1.我们直接使用安装命令 dnf install nginx 2.安装完成后启动nginx服务 # 启动 systemctl start nginx # 设置开机自启动 systemctl enable nginx# 重启 systemctl restart nginx# 查看状态 systemctl status nginx# 停止服务 systemctl stop nginx 3.查看版本确认安装成功…

Pytorch实现线性回归模型

在机器学习和深度学习的世界中&#xff0c;线性回归模型是一种基础且广泛使用的算法&#xff0c;简单易于理解&#xff0c;但功能强大&#xff0c;可以作为更复杂模型的基础。使用PyTorch实现线性回归模型不仅可以帮助初学者理解模型的基本概念&#xff0c;还可以为进一步探索更…

深信服超融合虚拟机备份报错显示准备备分镜像失败

问题&#xff1a;最近一段时间深信服超融合虚拟机在执行备份策略时总是报错&#xff0c;备份空间又还很富余。 解决办法&#xff1a; 1 删除备份失败虚拟机的所有备份 2 解绑该虚拟机的备份策略 可靠服务>>备份与CDP>> 找到备份策略>>点【编辑】>>…

刷机维修进阶教程---开机定屏 红字感叹号报错 写字库保资料 救砖 刷官方包保资料的步骤方法解析

在维修各种机型 中经常会遇到开机定屏 进不去系统,正常使用无故定屏进不去系统或者更新降级开机红色感叹号的一些故障机。但顾客需要报资料救砖的要求,遇到这种情况。我们首先要确定故障机型的缘由。是摔 还是更新降级 还是无故使用重启定屏等等。根据原因来对症解决。 通过…

springboot3整合redis

redis在我们的日常开发中是必不可少的&#xff0c;本次来介绍使用spring boot整合redis实现一些基本的操作&#xff1b; 1、新建一个spring boot项目&#xff0c;并导入相应的依赖&#xff1b; <dependency><groupId>org.springframework.boot</groupId><…

基于YOLOV8+Pyqt5无人机航拍太阳能电池板检测系统

1.YOLOv8的基本原理 YOLOv8是一种前沿的目标检测技术&#xff0c;它基于先前YOLO版本在目标检测任务上的成功&#xff0c;进一步提升了性能和灵活性&#xff0c;在精度和速度方面都具有尖端性能。在之前YOLO 版本的基础上&#xff0c;YOLOv8 引入了新的功能和优化&#xff0c;…

PDF 正确指定页码挂载书签后,书签页码对不上

这个问题与我的另一篇中方法一样 如何让一个大几千页的打开巨慢的 PDF 秒开-CSDN博客 https://blog.csdn.net/u013669912/article/details/138166922 另做一篇原因是一篇文章附带一个与该文章主题不相关的问题时&#xff0c;不利于被遇到该问题的人快速搜索发现以解决其遇到的…

C++笔试强训day9

目录 1.添加逗号 2.跳台阶 3.扑克牌顺子 day9的题目都比较简单&#xff0c;就不赘述了 1.添加逗号 链接 我的思路很清晰也很简单易懂&#xff1a; 把输入数据存入字符串string s&#xff0c;定义一个string ret&#xff0c;然后逆置string s&#xff0c;将s中的数一个一个…

C#窗体控件ColorDialog

介绍 可以获取颜色&#xff0c;然后对其他控件的颜色属性进行设置。 构造方式 直接把ColorDialog控件拖动到Form窗口。 常用属性 AllowFullOpen 可以控制是否使用自定义颜色&#xff0c;true表示可使用&#xff0c;否&#xff0c;表示不可使用。 AnyColor 如果为tr…

C#给PDF加文字水印

安装iTextSharp包。 using iTextSharp.text.pdf; using iTextSharp.text; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Pdf1 {internal class Program{public static v…