SpringBoot/Security重复使用request.getInputStream()
问题前提:您已经知道该方法得到的ServletInputStream只能读取一次,若后续需要继续使用则会报错Stream Closed。网上的很多方法并不完全可靠,因此本人踩坑后总结本文。
1、构造自定义HttpServletRequestWrapper
核心点:注意返回新的ServletInputStream一定也要重写read(byte b[], int off, int len),该方法是jackson等json工具内部调用的。
若不重写该方法会调用InputStream的该方法默认实现,从而调用您定义的无参read(),因此本方法保证可以使用。
代码:
package com.gx.utils.rabc;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* @author zlj(gx)
* @date 2023/7/14
*/
//每次刷新ServletInputStream
@Slf4j
public class InputStreamRewriteHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] requestBody;
/**
* Constructs a request object wrapping the given request.
*
* @param request The request to wrap
* @throws IllegalArgumentException if the request is null
*/
public InputStreamRewriteHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
//request.getInputStream()得到的是请求全部信息,但好像copy只能拷贝请求体的
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
//禁用该方法
@Override
@Deprecated
public int read() {
log.error("自定义类被read单个单词");
return -1;
}
@Override
public int read(byte b[], int off, int len){
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);
return byteArrayInputStream.read(b, off, len);
}
};
}
}
2、修改传参为该类,由于本人使用Security,则在CsrfFilter后新建一个Filter实现该功能。
package com.gx.security.filter;
import com.gx.utils.rabc.InputStreamRewriteHttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zlj(gx)
* @date 2023/7/14
*/
@Slf4j
@Component
public class RequestBodyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.info("RequestBodyFilter have changed object request to InputStreamRewriteHttpServletRequestWrapper");
filterChain.doFilter(new InputStreamRewriteHttpServletRequestWrapper(request), response);
}
}
3、Security配置类使用(新加这一行)
.addFilterAfter(requestBodyFilter, CsrfFilter.class)