不知道你们有没有对用户输入的东西进行过敏感校验,如果不进行校验,用户属于一些攻击脚本,那么我们的服务就挂逼啦!所以我们首先需要通过过滤器将用户的数据读出来进行安全校验,这里面涉及到一个动作,就是需要将用户的数据在过滤器中读出来,进行校验,通过之后再放行。
问题
如果我们的数据是get请求倒还好,但是如果是一些数据量比较大,我们需要通过post json的方式来说传递数据的时候,这个时候其实是通过流的方式传递的,如果在过滤器中将参数读取出来之后,然后放行,等到到Servlet的时候,@RequestBody是无法获取到数据的,因为post json使用流传递,流被读取之后就不存在了,所以我们在过滤器中读取之后,@ReqeustBody自然就读不到数据了,同时会报如下一个错误。
-
在过滤器中读取body中的数据
@WebFilter @Slf4j public class CheckUserFilter implements Filter {
1@Override 2public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { 3 4 HttpServletRequest request = (HttpServletRequest) req; 5 6 // 在过滤器中读取数据 7 BufferedReader reader = request.getReader(); 8 9 StringBuilder sb = new StringBuilder(); 10 11 String line; 12 while ((line = reader.readLine()) != null) { 13 sb.append(line); 14 } 15 reader.close(); 16 17 System.out.println(sb.toString()); 18 19 filterChain.doFilter(request, res); 20}}
-
出现异常,就是说内容已经被读取了,你不能调用了
{ "id":"1", "username":"bingfeng"} java.lang.IllegalStateException: UT010003: Cannot call getInputStream(), getReader() already called at io.undertow.servlet.spec.HttpServletRequestImpl.getInputStream(HttpServletRequestImpl.java:666) at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152) at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)
解决
- HttpServletRequestWrapper
那么出现这种问题怎么办呢?能不能通过一个中间的变量将这些数据保存下来,然后我们就可以一直读取了,这样不就解决了这个问题了吗?那保存在哪里呢?这个时候 HttpServletRequestWrapper 就排上用场了。
这个其实你可以把它理解为Request的包装类,Reqeust中有的方法它都有,我们通过继承这个类,重写该类中的方法,将body中的参数保存一个byte数组中,然后放行的时候将这个包装类传递进去,不就可以一直拿到参数了?
-
封装Request类
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
1private final byte[] body; 2 3/** 4 * 所有参数的集合 5 */ 6private Map<String, String[]> parameterMap; 7 8 9public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { 10 super(request); 11 BufferedReader reader = request.getReader(); 12 body = readBytes(reader); 13 parameterMap = request.getParameterMap(); 14} 15 16 17@Override 18public BufferedReader getReader() throws IOException { 19 20 ServletInputStream inputStream = getInputStream(); 21 22 if (null == inputStream) { 23 return null; 24 } 25 26 return new BufferedReader(new InputStreamReader(inputStream)); 27} 28 29@Override 30public Enumeration<String> getParameterNames() { 31 Vector<String> vector = new Vector<>(parameterMap.keySet()); 32 return vector.elements(); 33} 34 35@Override 36public ServletInputStream getInputStream() throws IOException { 37 38 if (body == null) { 39 return null; 40 } 41 42 final ByteArrayInputStream bais = new ByteArrayInputStream(body); 43 return new ServletInputStream() { 44 45 @Override 46 public boolean isFinished() { 47 return false; 48 } 49 50 @Override 51 public boolean isReady() { 52 return false; 53 } 54 55 @Override 56 public void setReadListener(ReadListener listener) { 57 58 } 59 60 @Override 61 public int read() throws IOException { 62 return bais.read(); 63 } 64 }; 65} 66 67/** 68 * 通过BufferedReader和字符编码集转换成byte数组 69 * 70 * @param br 71 * @return 72 * @throws IOException 73 */ 74private byte[] readBytes(BufferedReader br) throws IOException { 75 String str; 76 StringBuilder retStr = new StringBuilder(); 77 while ((str = br.readLine()) != null) { 78 retStr.append(str); 79 } 80 if (StringUtils.isNotBlank(retStr.toString())) { 81 return retStr.toString().getBytes(StandardCharsets.UTF_8); 82 } 83 return null; 84}}
-
将过滤器改造
@WebFilter @Slf4j public class CheckUserFilter implements Filter {
1@Override 2public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { 3 4 HttpServletRequest request = (HttpServletRequest) req; 5 6 BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request); 7 8 // 从Request的包装类中读取数据 9 BufferedReader reader = requestWrapper.getReader(); 10 11 StringBuilder sb = new StringBuilder(); 12 13 String line; 14 while ((line = reader.readLine()) != null) { 15 sb.append(line); 16 } 17 reader.close(); 18 19 System.out.println(sb.toString()); 20 21 filterChain.doFilter(requestWrapper, res); 22}}
经过这样的配置之后,我们即使在过滤器中获取了参数,请求也会到达Servlet。
如果基础知识IO那块不是很扎实的话,第一眼看到这个问题确实挺懵逼的。我也是百度之后解决的,确实值得记录一下,有时候我们会对所有请求进来的参数进行保存输出什么的,这个时候如果是post json数据的话,如果不是特别明白,可能也会出现这种问题。
<p style="text-align:center;font-weight:bold;color:#0e88eb;font-size:20px">日拱一卒,功不唐捐</p> <p style="text-align:center;font-weight:bold;color:#773098;font-size:16px">更多内容请关注</p>