原文地址:http://liwx2000.iteye.com/blog/1542431

原文作者:liwx2000

为了提高项目安全性,拦截非法访问,要给项目增加了一个过滤器,拦截所有的请求,校验是否有不安全因素。

这个过程就遇到了一个问题:ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。没办法,就去网上搜罗了一通,发现了一个超赞的解决方法。为了记录备案,无耻的转了过来,原文地址在上,内容在下。

1. 查看了下ServletRequest的说明,如下:

  1. /**
  2. * Retrieves the body of the request as binary data using
  3. * a {@link ServletInputStream}. Either this method or
  4. * {@link #getReader} may be called to read the body, not both.
  5. *
  6. * @return a {@link ServletInputStream} object containing
  7. * the body of the request
  8. *
  9. * @exception IllegalStateException if the {@link #getReader} method
  10. * has already been called for this request
  11. *
  12. * @exception IOException if an input or output exception occurred
  13. *
  14. */
  15.  
  16. public ServletInputStream getInputStream() throws IOException;
  17.  
  18. /**
  19. * Retrieves the body of the request as character data using
  20. * a <code>BufferedReader</code>. The reader translates the character
  21. * data according to the character encoding used on the body.
  22. * Either this method or {@link #getInputStream} may be called to read the
  23. * body, not both.
  24. *
  25. *
  26. * @return a <code>BufferedReader</code>
  27. * containing the body of the request
  28. *
  29. * @exception UnsupportedEncodingException if the character set encoding
  30. * used is not supported and the
  31. * text cannot be decoded
  32. *
  33. * @exception IllegalStateException if {@link #getInputStream} method
  34. * has been called on this request
  35. *
  36. * @exception IOException if an input or output exception occurred
  37. *
  38. * @see #getInputStream
  39. *
  40. */
  41.  
  42. public BufferedReader getReader() throws IOException;

两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,那么流读了一次就没有了,所以只能被调用一次。既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了。byte数组允许被多次读取,而不会丢失内容。下面使用byte数组将流的内容保存下来。

2.  工具方法:

先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。

代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]。

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5.  
  6. import javax.servlet.ServletInputStream;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletRequestWrapper;
  9.  
  10. import jodd.JoddDefault;
  11. import jodd.io.StreamUtil;
  12.  
  13. public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
  14.  
  15. private final byte[] body;
  16.  
  17. public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
  18. throws IOException {
  19. super(request);
  20. // body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);
         // 因为http协议默认传输的编码就是iso-8859-1,如果使用utf-8转码乱码的话,可以尝试使用iso-8859-1
         body = StreamUtil.readBytes(request.getReader(), "iso-8859-1");
  21. }
  22.  
  23. @Override
  24. public BufferedReader getReader() throws IOException {
  25. return new BufferedReader(new InputStreamReader(getInputStream()));
  26. }
  27.  
  28. @Override
  29. public ServletInputStream getInputStream() throws IOException {
  30. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  31. return new ServletInputStream() {
  32.  
  33. @Override
  34. public int read() throws IOException {
  35. return bais.read();
  36. }
  37. };
  38. }
  39.  
  40. }

3. 在Filter中的使用:

在Filter中将ServletRequest替换为ServletRequestWrapper

  1. public class HttpServletRequestReplacedFilter implements Filter {
  2.  
  3. @Override
  4. public void init(FilterConfig filterConfig) throws ServletException {
  5. //Do nothing
  6. }
  7.  
  8. @Override
  9. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  10. ServletRequest requestWrapper = null;
  11. if(request instanceof HttpServletRequest) {
  12. requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
  13. }
  14. if(null == requestWrapper) {
  15. chain.doFilter(request, response);
  16. } else {
  17. chain.doFilter(requestWrapper, response);
  18. }
  19.  
  20. }
  21.  
  22. @Override
  23. public void destroy() {
  24. //Do nothing
  25. }
  26.  
  27. }

ServletRequest中getReader()和getInputStream()只能调用一次的解决办法(转)的更多相关文章

  1. ServletRequest中getReader()和getInputStream()只能调用一次的解决办法

    转载:http://blog.sina.com.cn/s/blog_870cd7b90101fg58.html 最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数, ...

  2. 关于springmvc时request的getReader()和getInputStream()只能调用一次的解决办法

    最近准备在原有的SSM项目的基础上添加完善的日志分析,由于是APP的后台系统,之前在规划APP的时候,并没有在APP上做埋点的处理,而如果想要进行埋点处理的话,对于未能新升级的APP用户来说,就是去了 ...

  3. 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.

    由于 request中getReader()和getInputStream()只能调用一次 在项目中,可能会出现需要针对接口参数进行校验等问题. 因此,针对这问题,给出一下解决方案 实现方法:先将Re ...

  4. es6 Object.assign ECMAScript 6 笔记(六) ECMAScript 6 笔记(一) react入门——慕课网笔记 jquery中动态新增的元素节点无法触发事件解决办法 响应式图像 弹窗细节 微信浏览器——返回操作 Float 的那些事 Flex布局 HTML5 data-* 自定义属性 参数传递的四种形式

    es6 Object.assign   目录 一.基本用法 二.用途 1. 为对象添加属性 2. 为对象添加方法 3. 克隆对象 4. 合并多个对象 5. 为属性指定默认值 三.浏览器支持 ES6 O ...

  5. c++调用动态库失败解决办法

    c++调用动态库失败解决办法 之前写好的程序今天早上过来发现在服务器上出错了,于是就各种查问题,整整一个早上外加下午两个小时都在查这个问题,最终被我找到了问题: 在程序中我发现LoadLibrary( ...

  6. Android中View类OnClickListener和DialogInterface类OnClickListener冲突解决办法

    Android中View类OnClickListener和DialogInterface类OnClickListener冲突解决办法 如下面所示,同时导入这两个,会提示其中一个与另一个产生冲突. 1i ...

  7. .NET在IE9中页面间URL传递中文变成乱码的解决办法

     在.Net的项目中,鼠标点击查询按钮,转到查询页面,但URL中包含中文时,传到服务器端后,中文变成了乱码(只有IE9出现该问题).       尝试使用Server.UrlEncode()进行编码, ...

  8. Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法

    Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法 先上个图.语法提示支持到 Microsoft Dynamics xRM API 8.2 也就是cr ...

  9. electron-vue中使用iview 报错this. is readonly的解决办法

    title: electron-vue中使用iview 报错this. is readonly的解决办法 toc: false date: 2019-02-12 19:33:28 categories ...

随机推荐

  1. 50个Android开发技巧(10 为TextView加入样式)

    首先来看一个控件的例子: (原文地址:http://blog.csdn.net/vector_yi/article/details/24428085) 手机上类似这种场景你一定已经见过非常多次了,但有 ...

  2. 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&amp;颜色、光照与材质

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨)  ...

  3. repo的小结

    repo仅仅是google用Python脚本写的调用git的一个脚本,主要是用来下载.管理Android项目的软件仓库. 1. 下载 repo 的地址: http://android.git.kern ...

  4. HTML5硕士学习笔记

    如今,该集团经过培训的同事给大家HTML5,他出席了两个5训练日,大概过一次给我们,在一个很形象.同事们更感兴趣的是. 课后共享所有的课件.在热情的新技术,我想工作有一个良好的早晨,我决定重新学习课件 ...

  5. jsPlumb开发入门教程(实现html5拖拽连线)

    jsPlumb是一个强大的JavaScript连线库,它可以将html中的元素用箭头.曲线.直线等连接起来,适用于开发Web上的图表.建模工具等.它同时支持jQuery+jQuery UI.MooTo ...

  6. ASP.NET静态页生成方法(模板替换)

    本文实例讲述了ASP.NET静态页生成方法的一种简单方法,就是替换内容法. 适用场景 模板比较固定,页面替换内容较少. 基本原理 此方法中静态页生成用到的就是匹配跟替换了,首先得读取模板页的html内 ...

  7. Socket 理解

    TCP/IP要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协 ...

  8. 基于Spring MVC的简单HelloWorld实例

    1.导包 2.web.xml文件配置 3.包结构定义以及控制器的编写 4.xxxx-servlet文件配置 5.返回的视图(jsp)编写   6.源码 下载:http://download.csdn. ...

  9. 查询矩形范围内的"点"要素

    步骤 1,首先在含有主视图控件 ESRI.ArcGIS.Controls.AxMapControl mapCtrl_main 的主类中定义一个 IEnvelope 成员变量,用于记录鼠标在主视图控件画 ...

  10. Web ADF 编程步骤.

    从Web Controls 开始(工具来中的 ArcGIS Web Controls). 访问Resource Manager. 找到待访问的 Resource. 决定 Resource支持哪个 Fu ...