java.io.IOException: Stream closed,关于设置拦截器获取post请求的参数,出现的流关闭问题

问题描述:

就是最近在做接口拦截,判断post请求提交的参数有没有敏感词。但是在处理的过程中,就遇到了java.io.IOException: Stream closed这种类型的报错,其原因就是流只能被访问一次,在拦截器那边被访问之后,流就已经被关闭了,等到controller层再获取参数的时候,就会报错,无法获取参数。

解决思路:

1、添加过滤组件,你只能取出数据的同时,复制一份再放回去。
2、需要主要的是,复制流这个操作仅限于body数据格式(也就是Content-Type: application/json)是JSON的情况。如果是form-urllencoded这种key-value格式的,不能走复制流,否则controller会拿不到参数。如果是form表单(Content-Type: multipart/form-data)的情况,需要将流转换成MultipartFile格式,要不然在controller层依旧会取不到上传的文件数据。

代码展示:

1、添加一个工具类,获取到post请求的body
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * HttpContextUtils
 *
 * @author taojuan
 */
public class HttpContextUtils extends HttpServletRequestWrapper {

    /**
     * 输入流
     */
    private final byte[] bytes;

    public HttpContextUtils(HttpServletRequest request) throws IOException {
        super(request);

	byteArrayOutputStream.toByteArray()
        bytes = getBodyString(request).getBytes(StandardCharsets.UTF_8);

    }


    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try (
                InputStream inputStream = cloneInputStream(request.getInputStream());
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
        ) {
            String line = "";
            while (Objects.nonNull((line = reader.readLine()))) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new RuntimeException("输入流读取出错");
        }
        return sb.toString();
    }

    /**
     * 输入流复制
     *
     * @param inputStream
     * @return
     */
    private InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException("复制输入流读取出错");
        }
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重写父方法,返回新的输入流
     *
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream copyStream = new ByteArrayInputStream(bytes);
        /**
         * 新的输入流
         */
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return copyStream.read();
            }

            /**
             * 未读状态
             * @return
             */
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

2、添加一个过滤器
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <b><code>HttpServelRequestFilter</code></b>
 * <p/>
 * Description
 * <p/>
 * <b>Creation Time:</b> 2022/12/2 上午11:41.
 *
 * @author taojuan
 * @since cmicsed-dyprdp-be 0.2.0
 */
@WebFilter(urlPatterns = {"/*"})
@Slf4j
public class HttpServletRequestFilter implements Filter {
    // {"/api/v1/closedcircle/*","/api/v1/qualityService/*","/api/v1/qualityLightOn/*","/api/v1/audiocenter/*","/api/v1/listenaudio/comment"}

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        String token = request.getHeader("token");
        response.setHeader("token", token);
        String contentType = servletRequest.getContentType();
        String method = "multipart/form-data";

        if (contentType != null && contentType.contains(method)) {
            //重点!!!!!这里通过spring的轮子,实现request的转换,
            MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
            MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
            // 将转化后的 request 放入过滤链中
            request = multipartRequest;

        }

        ServletRequest requestWrapper = new HttpContextUtils(request);

        try {
            log.info("获取到的输入流:{}", requestWrapper.getInputStream());
        } catch (Exception e) {
            log.error("异常:", e);
        }
        filterChain.doFilter(requestWrapper, servletResponse);

    }

    @Override
    public void destroy() {

    }


}
3、在拦截器里获取post请求的body,进行一些敏感词的判断拦截

主要是调用这个办法: String postParam = new HttpContextUtils(httpServletRequest).getBodyString(httpServletRequest);

@Component
@Slf4j
public class ApiInterceptor implements HandlerInterceptor {
 @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        // 从 http 请求头中取出签名
        try {
            //post请求,body中的参数
            String postParam = new HttpContextUtils(httpServletRequest).getBodyString(httpServletRequest);

            log.info("获取到的post请求参数:{}", postParam);

            List<String> list = new ArrayList<>();

            if (!postParam.isEmpty()) {
                List<String> temp = wordMapper.selectSensitiveWord();

                //对比敏感词库,是否有敏感词
                if (postParam != null && postParam.trim().length() > 0) {
                    for (String str : temp) {
                        if (postParam.contains(str)) {
                            list.add(str);
                        }
                    }
                }
            }

            if (!list.isEmpty()) {
                // throw new IOException("您发送请求中的参数中含有非法字符");
                throw new MeshNetwornException("您发送请求中的参数中含有非法字符", list);
            }
        } catch (IOException io) {
            throw new IOException("您发送请求中的参数中含有非法字符");
        } catch (MeshNetwornException e) {
            throw new MeshNetwornException("您发送请求中的参数中含有敏感字符", e.getData());
        } catch (Exception e) {
            e.printStackTrace();
            return true;
        }

        return true;

    }

}
4、最后需要在启动类上添加注解,开启过滤组件
@ServletComponentScan

如果过滤不起作用,可在组件扫描那里加上HttpServletRequestFilter这个过滤器所在的目录,如下图,我的文件放在该包下

@ServletComponentScan("com.commons.component")
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
		TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
	}
}
5、如果遇到java.lang.ClassNotFoundException: org.apache.commons.fileupload.disk.DiskFileItemFactory这个问题,可能就是pom文件确实下面的依赖包,添加即可。
<dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.2.1</version>
 </dependency>

上面就是我遇到Stream closed问题解决的所有过程了,问题不同,仅供参考。
下面是解决问题时,主要参考的几个博文:
1、http://t.csdn.cn/NbNUB
2、http://t.csdn.cn/4N3Th
3、http://t.csdn.cn/fNaAr

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值