问题描述
A系统和B系统需要通讯,A系统对参数加密,B系统在Fliter拦截器中拦截后,读取流,解析参数做数据校验处理,正常读取解析后,发现流到达Controller层后,报错,请求流已经被读取,为空的问题。
解决办法
对于Request请求流只能被读取一次的问题,解决问题的主题思想,将Request请求流复制保存到一个final 字节数组中,这样,request请求流到达Controller后就不会为空,问题解决。
新建 BodyReaderHttpServletRequestWrapper类,继承HttpServletRequestWrapper,重写getReader()和getInputStream()方法,将复制的流保存到byte[] body中,还可以对请求的流做一些XSS攻击过滤处理,代码如下:
package com.test.wrapper;
import com.test.util.XssUtils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import jodd.io.StreamUtil;
/*
*@author lucasliang
* filter Read the URL parameter and copy the request body stream transformation class.
* */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
/*
*@param: [request]
*@return
*@author lucasliang
*@date
*@Description save the post request parameter to byte[]
*/
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
throws IOException {
super(request);
body = StreamUtil.readBytes(request.getReader(), "utf-8");
}
/*
*@param: []
*@return java.io.BufferedReader
*@author lucasliang
*@date 11/12/2018
*@Description obtain the request header
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/*
*@param: []
*@return javax.servlet.ServletInputStream
*@author lucasliang
*@date 11/12/2018
*@Description obatin request inputstream
*/
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(body).getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
/*
*@param: [readListener]
*@return void
*@author lucasliang
*@date 11/12/2018
*@Description set read listener
*/
@Override
public void setReadListener(ReadListener readListener) {
// do nothing
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
/*
*@param: [servletInputStream]
*@return java.lang.String
*@author lucasliang
*@date 25/01/2019
*@Description turn input param to string
*/
private String inputHandlers(byte[] bytes) {
return XssUtils.stripXss(new String(bytes));
}
}
查看源码可知
ByteArrayInputStream 继承Inpustream,实现了markSupported方法返回true同时实现了reset方法,而ServletInputStream 没有重写markSupported方法和reset方法,Inputstream markSupported方法返回false;
XssUtils工具类
package com.test.util;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
/**
* @author lucasliang
* @date 20/02/2019 5:12 afternoon
* @Description XssUtils
*/
public class XssUtils {
private XssUtils() {
}
private static List<Pattern> patterns = null;
/*
*@param: []
*@return java.util.List<java.lang.Object[]>
*@author lucasliang
*@date 25/01/2019
*@Description regex
*/
private static List<Object[]> getXssPatternList() {
List<Object[]> ret = new ArrayList<>();
ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
ret.add(new Object[]{"eval\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"expression\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
ret.add(new Object[]{"<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{
"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
ret.add(new Object[]{
"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick"
+ "|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|"
+ "onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|"
+ "onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|"
+ "onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|"
+ "onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|"
+ "onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|"
+ "onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
return ret;
}
/*
*@param: []
*@return java.util.List<java.util.regex.Pattern>
*@author lucasliang
*@date 25/01/2019
*@Description get all regex
*/
private static List<Pattern> getPatterns() {
if (patterns == null) {
List<Pattern> list = new ArrayList<>();
String regex;
Integer flag;
int arrLength;
for (Object[] arr : getXssPatternList()) {
arrLength = arr.length;
for (int i = 0; i < arrLength; i++) {
regex = (String) arr[0];
flag = (Integer) arr[1];
list.add(Pattern.compile(regex, flag));
}
}
patterns = list;
}
return patterns;
}
/***
*@param: [value]
*@return java.lang.String
*@author lucasliang
*@date 23/01/2019
*@Description strip xss
*/
public static String stripXss(String value) {
if (StringUtils.isNotBlank(value)) {
Matcher matcher;
for (Pattern pattern : getPatterns()) {
matcher = pattern.matcher(value);
// 匹配
if (matcher.find()) {
// 删除相关字符串
value = matcher.replaceAll("");
}
}
value = value.replaceAll("<", "<").replaceAll(">", ">");
}
return value;
}
}
总结
只要思想不滑坡,方法总比困难多!