SpringBoot 拦截器获取http请求参数—— 所有骚操作基础

本文主要是介绍SpringBoot 拦截器获取http请求参数—— 所有骚操作基础,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • SpringBoot 拦截器获取http请求参数—— 所有骚操作基础
    • 获取http请求参数是一种刚需
    • 定义拦截器获取请求
    • 为什么拦截器会重复调两遍呢?
    • ServletInputStream(CoyoteInputStream) 输入流无法重复调用
    • 自定义 HttpServletRequestWrapper
    • 总结一下 展望一下

获取http请求参数是一种刚需

我想有的小伙伴肯定有过获取http请求的需要,比如想

  1. 前置获取参数,统计请求数据
  2. 做服务的接口签名校验
  3. 敏感接口监控日志
  4. 敏感接口防重复提交

等等各式各样的场景,这时你就需要获取 HTTP 请求的参数或者请求body,一般思路有两种,一种就是自定义个AOP去拦截目标方法,第二种就是使用拦截器。整体比较来说,使用拦截器更灵活些,因为每个接口的请求参数定义不同,使用AOP很难细粒度的获取到变量参数,本文主线是采用拦截器来获取HTTP请求。

定义拦截器获取请求

基于 spring-boot-starter-parent 2.1.9.RELEASE

看起来这个很简单,这里就直接上code,定义个拦截器

Java

/*** @summary HTTP请求拦截器*/
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求参数String queryString = request.getQueryString();log.info("请求参数:{}", queryString);//获取请求bodybyte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());String body = new String(bodyBytes, request.getCharacterEncoding());log.info("请求体:{}", body);return true;}
}

然后把这个拦截器配置一下中:

Java

/*** WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");}
}

定义个接口测试一下

Java

/*** @summary 提交测试接口*/
@Slf4j
@RestController
public class MyHTTPController {@GetMapping("/v1/get")public void get(@RequestParam("one") String one,@RequestParam("two") BigDecimal number) {log.info("参数:{},{}", one, number);}@PostMapping("/v1/post")public void check(@RequestBody User user) {log.info("{}", JSON.toJSONString(user));}
}

GET请求获取请求参数示例:

POST请求获取请求Body示例:

我们发现拦截器在获取HTTP请求的body时出现了 400;同时也发现拦截器竟然走了两遍,这又是咋回事呢?

为什么拦截器会重复调两遍呢?

其实是因为 tomcat截取到异常后就转发到/error页面,就在这个转发的过程中导致了springmvc重新开始DispatcherServlet的整个流程,所以拦截器执行了两次,我们可以看下第二次调用时的url路径:

ServletInputStream(CoyoteInputStream) 输入流无法重复调用

而之前出现的 Required request body is missing 错误 其实是ServletInputStream被读取后无法第二次再读取了,所以我们要把读取过的内容存下来,然后需要的时候对外提供可被重复读取的ByteArrayInputStream。

对于MVC的过滤器来说,我们就需要重写 ServletInputStream 的 getInputStream()方法。

自定义 HttpServletRequestWrapper

为了 重写 ServletInputStream 的 getInputStream()方法,我们需要自定义一个 HttpServletRequestWrapper :

Java

/**
* @summary 自定义 HttpServletRequestWrapper 来包装输入流
*/
public class XinHttpServletRequestWrapper extends HttpServletRequestWrapper {/*** 缓存下来的HTTP body*/private byte[] body;public XinHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);body = StreamUtils.copyToByteArray(request.getInputStream());}/*** 重新包装输入流* @return* @throws IOException*/@Overridepublic ServletInputStream getInputStream() throws IOException {InputStream bodyStream = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bodyStream.read();}/*** 下面的方法一般情况下不会被使用,如果你引入了一些需要使用ServletInputStream的外部组件,可以重点关注一下。* @return*/@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readListener) {}};}@Overridepublic BufferedReader getReader() throws IOException {InputStream bodyStream = new ByteArrayInputStream(body);return new BufferedReader(new InputStreamReader(getInputStream()));}
}

然后定义一个 DispatcherServlet子类来分派 上面自定义的 XinHttpServletRequestWrapper :

Java

/**
* @summary 自定义 DispatcherServlet 来分派 XinHttpServletRequestWrapper
*/
public class XinDispatcherServlet extends DispatcherServlet {/*** 包装成我们自定义的request* @param request* @param response* @throws Exception*/@Overrideprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {super.doDispatch(new XinHttpServletRequestWrapper(request), response);}
}

然后配置一下:

Java

/*** WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");}@Bean@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet() {return new XinDispatcherServlet();}
}

再调用一下 POST请求:

请求成功!

总结一下 展望一下

如果你想对HTTP请求做些骚操作,那么前置获取HTTP请求参数是前提,为此文本给出了使用MVC拦截器获取参数的样例。

在获取HTTP Body 的时候,出现了 Required request body is missing 的错误,同时拦截器还出现执行了两遍的问题,这是因为 ServletInputStream被读取了两遍导致的,tomcat截取到异常后就转发到 /error 页面 被拦截器拦截到了,拦截器也就执行了两遍。

为此我们通过自定义 HttpServletRequestWrapper 来包装一个可被重读读取的输入流,来达到期望的拦截效果。

在获取到HTTP的请求参数后,我们可以前置做很多操作,比如常用的服务端接口签名验证,敏感接口防重复请求等等。

这篇关于SpringBoot 拦截器获取http请求参数—— 所有骚操作基础的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1137949

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2