解读HttpServletRequestWrapper处理request数据流多次读取问题

 更新时间:2024年10月09日 10:07:26   作者:小旋风-java  
在Java Web开发中,获取HTTP请求参数是常见需求,本文详细讨论了通过POST方式获取参数的两种主要方法:使用request.getParameter()适用于application/x-www-form-urlencoded和multipart/form-data内容类型;而对于application/json类型的数据

本次讨论post方式获取参数,request.getInputStream()获取一次以后不能第二次获取,以及request.getParameter()与request.getInputStream()也存在这种情况

一、获取请求参数

  • 1、request.getParameter()
  • 2、request.getInputStream()或request.getReader()

二、请求方contentType

1、application/x-www-form-urlencoded

@RequestMapping("/testWwwFrom")
@ResponseBody
public Book testWwwFrom(Book req, HttpServletRequest servletRequest) {
    logger.info("查询所有1用户信息" + JSON.toJSONString(req));
    return req;
}

2、multipart/form-data

@RequestMapping("/testFormData")
@ResponseBody
public Book testFormData(Book req, HttpServletRequest servletRequest) {
    logger.info("查询所有1用户信息" + JSON.toJSONString(req));
    return req;
}

3、application/json

@RequestMapping("/testJson")
@ResponseBody
public Book testJsonFrom(@RequestBody Book req, HttpServletRequest servletRequest) {
    logger.info("查询所有1用户信息" + JSON.toJSONString(req));
​
    return req;
}
  • 1、request.getParameter()对应application/x-www-form-urlencoded
  • 2、request.getInputStream()或request.getReader()对应application/json

那么现在有一个这样的需求如果是form表单就获取formToken然后和缓存的进行CRSF防护

思路根据上面说的form表单参数获取分为2大类。

其中request.getInputStream()获取一次以后不能第二次获取,以及request.getParameter()与request.getInputStream()也存在这种情况。

  • 1、application/x-www-form-urlencoded以及multipart/form-data通过request.getParameter()获取
  • 2、application/json通过request.getInputStream()或request.getReader()获取,这种要依赖HttpServletRequestWrapper
package com.study.ju.web.interceptor;
​
import org.apache.commons.io.IOUtils;
​
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
​
/**
 * <p>https://blog.csdn.net/kaizhangzhang/article/details/97900961</p>
 *
 * 
 * @version v 0.1 2023/5/29 15:37
 */
public class ResettableServletRequestWrapper extends HttpServletRequestWrapper {
    //保存流中的数据
    private byte[] data;
​
    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request
     * @throws IllegalArgumentException if the request is null
     */
    public ResettableServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //从流中获取数据
        data = IOUtils.toByteArray(request.getInputStream());
    }
​
    @Override
    public ServletInputStream getInputStream() throws IOException {
        //在调用getInputStream函数时,创建新的流,包含原先数据流中的信息,然后返回
        return new NewServletInputStream(new ByteArrayInputStream(data));
    }
​
    class NewServletInputStream extends ServletInputStream{
        private InputStream inputStream;
​
        public NewServletInputStream(InputStream inputStream){
            this.inputStream = inputStream;
        }
​
        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
​
        @Override
        public boolean isFinished() {
            return false;
        }
​
        @Override
        public boolean isReady() {
            return false;
        }
​
        @Override
        public void setReadListener(ReadListener readListener) {
​
        }
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
​
​
}
package com.study.ju.web.interceptor;
​
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
​
/**
 * <p>form表单提交校验token</p>
 *
 * 
 * @version v 0.1 2023/5/29 13:45
 */
public class FormSubmitTokenInterceptor implements HandlerInterceptor {
​
​
    private String formSubmitTokenInterceptorUrls = "[\"/testJson\",\"/test/testJson\",\"/test/testWwwFrom\",\"/test/testRequestParam\"]";
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = getRequestURI(request);
        if ("POST".equals(request.getMethod().toUpperCase()) && StringUtils.isNotEmpty(formSubmitTokenInterceptorUrls)) {
            List<String> formInterceptorUrls = JSONArray.parseArray(formSubmitTokenInterceptorUrls, String.class);
            if (CollectionUtils.isNotEmpty(formInterceptorUrls)) {
                boolean contains = formInterceptorUrls.stream().anyMatch(e -> e.contains(requestURI));
                if (contains) {
                    String contentType = request.getContentType();
                    System.out.println(contentType);
                    if ("application/json".equalsIgnoreCase(contentType)) {
                        ResettableServletRequestWrapper resettableServletRequestWrapper = new ResettableServletRequestWrapper(request);
                        String bodyParam = IOUtils.toString(resettableServletRequestWrapper.getInputStream());
                        System.out.println(bodyParam);
                        if (StringUtils.isNotEmpty(bodyParam)) {
                            JSONObject jsonObject = JSONObject.parseObject(bodyParam);
                            if (jsonObject.containsKey("formToken")) {
                                String formToken = (String) jsonObject.get("formToken");
                                System.out.println("formToken:"+formToken);
                            }
                        }
​
                    } else {
                        if (StringUtils.isNotEmpty(request.getParameter("formToken"))) {
                            System.out.println("formToken2:"+request.getParameter("formToken"));
                        }
                        System.out.println("name:"+request.getParameter("name"));
                    }
​
​
                }
​
            }
        }
        return true;
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
​
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
​
    }
​
    /**
     * 获取用户请求的path,不带contextPath
     * 如:/index.htm
     */
    public static String getRequestURI(HttpServletRequest request) {
        return StringUtils.replace(request.getRequestURI(), request.getContextPath(), "");
    }
}

web.xml放在前面

<filter>
   <filter-name>requestFilter</filter-name>
   <filter-class>com.study.ju.web.Filter.HttpServletRequestReplacedFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>requestFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • IDEA项目使用SpringBoot+MyBatis-Plus的方法

    IDEA项目使用SpringBoot+MyBatis-Plus的方法

    这篇文章主要介绍了IDEA项目使用SpringBoot+MyBatis-Plus的方法,本文分步骤通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • Springboot集成Proguard生成混淆jar包方式

    Springboot集成Proguard生成混淆jar包方式

    本文介绍了两种Java代码混淆工具:ClassFinal和ProGuard,ClassFinal是一个字节码加密工具,但需要额外的加密包,使用复杂,ProGuard是一款开源的Java代码混淆工具,可以有效地提高代码的安全性,但对Spring框架的注解处理不够完善
    2024-11-11
  • mybatis类型转换器如何实现数据加解密

    mybatis类型转换器如何实现数据加解密

    这篇文章主要介绍了mybatis类型转换器如何实现数据加解密,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java9 Stream Collectors新增功能(小结)

    Java9 Stream Collectors新增功能(小结)

    这篇文章主要介绍了Java9 Stream Collectors新增功能(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • java和 javaw 及 javaws的区别解析

    java和 javaw 及 javaws的区别解析

    这篇文章主要介绍了java和 javaw 及 javaws的区别解析,本文通过实例给大家详细介绍,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • 浅谈Mybatis Plus的BaseMapper的方法是如何注入的

    浅谈Mybatis Plus的BaseMapper的方法是如何注入的

    我们在用的时候经常就是生产自定义的Mapper继承自BaseMapper,那么BaseMapper怎么被注入到mybatis里的,本文就详细的介绍一下,感兴趣的可以了解一下
    2021-09-09
  • Java之JFrame输出Helloworld实例

    Java之JFrame输出Helloworld实例

    这篇文章主要介绍了Java之JFrame输出Helloworld的方法,以输出Helloworld的实例分析了JFrame的简单入门技巧,需要的朋友可以参考下
    2015-02-02
  • JavaSE系列基础包装类及日历类详解

    JavaSE系列基础包装类及日历类详解

    这篇文章主要介绍的是JavaSE中常用的基础包装类以及日历类的使用详解,文中的示例代码简洁易懂,对我们学习JavaSE有一定的帮助,感兴趣的小伙伴快来跟随小编一起学习吧
    2021-12-12
  • Java使用正则表达式删除所有HTML标签的方法示例

    Java使用正则表达式删除所有HTML标签的方法示例

    这篇文章主要介绍了Java使用正则表达式删除所有HTML标签的方法,结合完整实例形式分析了java针对HTML页面元素script标签、style标签、html标签等的正则匹配相关操作技巧,需要的朋友可以参考下
    2017-06-06
  • Java中使用开源库JSoup解析HTML文件实例

    Java中使用开源库JSoup解析HTML文件实例

    这篇文章主要介绍了Java中使用开源库JSoup解析HTML文件实例,Jsoup是一个开源的Java库,它可以用于处理实际应用中的HTML,比如常见的HTML格式化就可以用它来实现,需要的朋友可以参考下
    2014-09-09

最新评论