问题描述:
就是最近在做接口拦截,判断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这个过滤器所在的目录,如下图,我的文件放在该包下文章来源:https://www.toymoban.com/news/detail-588287.html
@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文章来源地址https://www.toymoban.com/news/detail-588287.html
到了这里,关于java.io.IOException: Stream closed,关于设置拦截器获取post请求的参数,出现的流关闭问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!