过滤器request参数中payload丢失问题

Java Spring 过滤器 request 参数中 payload 丢失问题

省流版:

org.springframework.boot:spring-boot:2.1.8.RELEASE && org.springframework:spring-web:5.1.9.RELEASE​版本中的org.springframework.web.filter.HiddenHttpMethodFilter#doFilterInternal​方法读取HttpServletRequest.InputStream​流导致后续过滤器和拦截器无法再次读取InputStream​。

起因:

最近有一个需要在前端对接口数据进行加密,再后端接口进行解密的需求,所以打算用rsa,前端请求数据公钥加密后,后端在拦截器中解密,而当前端请求到达后端自定义拦截器处时,读取出InputStream​为空,只能通过request.getParameter​取出对应的参数值,但是无法取出body​中的数据,也就是即无法获取json​数据

排查过程:

  1. (此步方向错误)首先,对上一级的过滤器进行排查,初步怀疑是上一级过滤器中的request​包装类代码逻辑错误,在包装过程中没有正确存储InputStream​所以搭建了一个最小环境测试包装类(与发生问题的环境版本不同),发现在其他环境中包装类能正常重读InputStream​数据。
  2. 再者,对项目中其他的过滤器进行屏蔽,发现并无效果,依然无法读取InputStream​数据。
  3. 通过IDEA Debugger Frames​逐个方法反推,逐步定位发现如果发起的是POST请求(GET请求也无法传递body​)在org.springframework.web.filter.OncePerRequestFilter​的子类中org.springframework.web.filter.HiddenHttpMethodFilter#doFilterInternal​方法执行了request.getParameter(this.methodParam);​导致InputStream​数据丢失。

解决方案:

  1. 屏蔽HiddenHttpMethodFilter​过滤器,使其不能读取InputStream​,但是可能存在一些安全性的问题

       a. 屏蔽方法有两种,第一种是通过代码配置,参考链接、参考代码如下。
       

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Configuration
    public class FilterConfig {
    @Bean
    public FilterRegistrationBean registration(HiddenHttpMethodFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
    }
    }

       b. 通过配置关闭,在Spring boot​2.1.0版本后可以通过配置关闭该过滤器,在2.2​版本后默认关闭,屏蔽配置:​spring.mvc.hiddenmethod.filter.enabled: false​。

  2. 通过转换为ServletServerHttpRequest​使用getBody​方法获取body​数据,代码如下。

    1
    new String(StreamUtils.copyToByteArray(new ServletServerHttpRequest((HttpServletRequest) servletRequest).getBody()))
  3. 重新实现HiddenHttpMethodFilter​中的doFilterInternal​方法,在读取之前将request​进行包装(未实践)

  4. HiddenHttpMethodFilter​调用之前将request​进行包装(未实践)

参考:

  1. HiddenHttpMethodFilter consumes input stream of POST request
  2. ServletServerHttpRequest.getBody
  3. Disable auto-configuration of HiddenHttpMethodFilter by default
  4. Why is HttpServletRequest inputstream empty?