解决getReader() has already been called for this request
在 Filter 中对 request 中的 body 进行参数签名校验
会报如下错误:
getReader() has already been called for this request
原因是
request.getReader() 和 request.getInputStream() 都是只能调用一次
并且 getReader() 方法底层也是调用 getInputStream() 来实现的.
所以我们要使用 HttpServletRequestWrapper 来实现自定义的 CustomHttpServletRequestWrapper, 把 body 保存在 CustomHttpServletRequestWrapper 中, 并且重写 getInputStream() 方法
重写 HttpServletRequestWrapper
package com.fosung.pb.village.config; import org.apache.commons.fileupload.servlet.ServletFileUpload; 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.InputStreamReader; import java.nio.charset.StandardCharsets; public class MyRequestWrapper extends HttpServletRequestWrapper { private byte[] body; public MyRequestWrapper(HttpServletRequest request) throws IOException { super(request); if(ServletFileUpload.isMultipartContent(request)){ return; } StringBuilder sb = new StringBuilder(); String line; BufferedReader reader = request.getReader(); while ((line = reader.readLine()) != null) { sb.append(line); } String body = sb.toString(); this.body = body.getBytes(StandardCharsets.UTF_8); } public String getBody() { return new String(this.body , StandardCharsets.UTF_8) ; } @Override public ServletInputStream getInputStream() { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read(){ return bais.read(); } }; } @Override public BufferedReader getReader(){ return new BufferedReader(new InputStreamReader(this.getInputStream())); } }
Filter 使用
package com.fosung.pb.village.config; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author lanx * @date 2022/4/11 */ @Slf4j @Component @WebFilter(filterName = "AntiSqlInjectionfilter", urlPatterns = "/*") public class AntiSqlInjectionfilter implements Filter { public void destroy() { // TODO Auto-generated method stub } public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; MyRequestWrapper myRequestWrapper = null; // 获取请求body try { myRequestWrapper = new MyRequestWrapper(req); } catch (IOException e) { log.error("get request body exception", e); throw new RuntimeException(e); } if ("POST".equalsIgnoreCase(req.getMethod())) { String param = myRequestWrapper.getBody(); chain.doFilter(myRequestWrapper, response); } else { chain.doFilter(myRequestWrapper, response); } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。