思路就是这样,在写的过程中,但是由于我们从request中调用了getInputStream(),这样会导致,在controller层,我们使用@RequestBody注解获取数据时,出现getInputStream() has already been called for this request异常。因为request中的getRead() 和 getInputStream()在读取一次后标记为-1,无法再次被读取,而在@RequestBody在ServletServerHttpRequest中,也调用了getInputStream()方法所以如果数据在filter中被读取,将无法在@RequestBody中再次读取。当然如果传输的参数中存在文件,任然会有getInputStream() has already been called for this request异常,这个时候可以将自己文件上传的url过滤掉。
我们设置一个私有变量用于存储原始request的body数据或者param数据,并重写getReader()和getInputStream()方法,这样我们在filter处理了body数据,又能在controller中用@RequestBody中获取到数据了此时解决方案,将request进行包装(装饰者模式),并重写getInputStream()和getRead()方法。
filter类:
@Configuration
@Slf4j
public class HttpRequestParamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
log.info("HttpRequestParamFilter init...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//此处可以查询出所有的文件导入接口,排除掉文件上传导致ResponseBody失效引发的getInputStream() has already been called for this request异常
List<String> uploadFileUrlList = new ArrayList<>();
uploadFileUrlList.add("/ios/sysmgr/template/import");
uploadFileUrlList.add("/ios/auth/app/upload");
uploadFileUrlList.add("/doc.html");
boolean exitUrl = true;
for (String url : uploadFileUrlList) {
if (url.equals(request.getServletPath())) {
exitUrl = false;
break;
}
}
//获取param 参数并校验param中的参数
String result = "";
IRequestParamWrapper iRequestParamWrapper = new IRequestParamWrapper(request);
String paramData = iRequestParamWrapper.getParam();
if (paramData != null && !StringUtils.isEmpty(paramData)) {
log.info("param===>{}", paramData);
Map<String, String> resultMap = new HashMap<>(2);
resultMap.put("code", "1");
resultMap.put("illegalData", paramData);
response.setContentType("application/x-www-form-urlencoded;charset=UTF-8");
response.getOutputStream().write(JSON.toJSONString(resultMap).getBytes("UTF-8"));
return;
}
//获取body参数
IRequestBodyWrapper iRequestWrapper = null;
if (exitUrl) {
iRequestWrapper = new IRequestBodyWrapper(request);
String bodyData = iRequestWrapper.getBody();
if (bodyData != null && !StringUtils.isEmpty(bodyData)) {
log.info("body====>{}", bodyData);
result = CheckRequestParamUtil.regexBody(bodyData);
}
if (result != null && !StringUtils.isEmpty(result)) {
log.info("body存在非法输入");
Map<String, String> resultMap = new HashMap<>(2);
resultMap.put("code", "1");
resultMap.put("illegalData", result);
response.setContentType(request.getContentType() + ";charset=UTF-8");
response.getOutputStream().write(JSON.toJSONString(resultMap).getBytes("UTF-8"));
return;
}
}
filterChain.doFilter(iRequestWrapper != null ? iRequestWrapper : request, servletResponse);
}
@Override
public void destroy() {
log.info("HttpRequestParamFilter destroy....");
}
}
//获取body数据
@Slf4j
public class IRequestBodyWrapper extends HttpServletRequestWrapper {
/**
* 记录request的body数据
*/
private final String body;
/**
* 获取request body数据
*
* @param request
* @throws IOException
*/
public IRequestBodyWrapper(HttpServletRequest request) throws IOException {
super(request);
this.body = CheckRequestParamUtil.readHttpRequestBody(request);
}
public String getBody() {
return body;
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
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 byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
//获取param数据,并校验
@Slf4j
public class IRequestParamWrapper extends HttpServletRequestWrapper {
/**
* 记录request的body数据
*/
private final String param;
/**
* 获取request param数据
*
* @param request
* @throws IOException
*/
public IRequestParamWrapper(HttpServletRequest request) throws IOException {
super(request);
this.param = CheckRequestParamUtil.readHttpRequestParam(request);
System.out.println("param=" + param);
}
public String getParam() {
return param;
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(param.getBytes());
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 byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
//正则匹配工具类
@Slf4j
public class CheckRequestParamUtil {
/**
* 缓冲区大小
*/
private static final int BUFFER_SIZE = 1024 * 8;
/**
* 匹配<script></script>标签
*/
public static final String SCRIPT = "<script[^>]*>([^<]*)</script>";
/**
* 匹配<div></div> 标签正则
*/
public static final String DIV = "<div[^>/]*>(.*?)</div>";
/**
* 匹配<form></form> 标签正则
*/
public static final String FORM = "<form[^>]*>([^<]*)</form>";
/**
* 匹配<a></a> 标签正则表达式
*/
public static final String A = "<a[^>]*>([^<]*)</a>";
/**
* 匹配input框标签
*/
public static final String INPUT = "<input[^>/]*/>";
public static final Map<String, String> regexMap = new HashMap<>(16);
static {
regexMap.put("<script></script>", SCRIPT);
regexMap.put("<div></div>", DIV);
regexMap.put("<input />", INPUT);
regexMap.put("<a></a>", A);
regexMap.put("<form></form>", FORM);
}
/**
* 获取request请求中的参数
*
* @param request
* @return
*/
public static String readHttpRequestParam(HttpServletRequest request) {
Map<String, Object> requestParamMap = new HashMap<>(16);
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length > 0) {
String paramValue = paramValues[0];
if (paramValue.length() != 0) {
requestParamMap.put(paramName, paramValue);
}
}
}
Set<Map.Entry<String, Object>> set = requestParamMap.entrySet();
StringBuffer result = new StringBuffer();
for (Map.Entry entry : set) {
log.debug(entry.getKey() + ":" + entry.getValue());
String temp = regexBody(entry.getValue().toString());
if (temp != null || !StringUtils.isEmpty(temp)) {
result.append(temp);
}
}
return result.toString();
}
/**
* 读取body中的数据
*
* @param request
* @return
* @throws IOException
*/
public static String readHttpRequestBody(HttpServletRequest request) throws IOException {
BufferedReader bufferedReader = request.getReader();
StringWriter writer = new StringWriter();
write(bufferedReader, writer);
return writer.getBuffer().toString();
}
public static long write(Reader reader, Writer writer) throws IOException {
return write(reader, writer, BUFFER_SIZE);
}
public static long write(Reader reader, Writer writer, int bufferSize) throws IOException {
int read;
long total = 0;
char[] buf = new char[bufferSize];
while ((read = reader.read(buf)) != -1) {
writer.write(buf, 0, read);
total += read;
}
return total;
}
/**
* 校验是否存在完整html的标签数据
*
* @param body body数据
* @return java.lang.String 提示信息
*/
public static String regexBody(String body) {
Iterator<Map.Entry<String, String>> entryIterator = regexMap.entrySet().iterator();
StringBuffer result = new StringBuffer();
while (entryIterator.hasNext()) {
Map.Entry<String, String> entry = entryIterator.next();
Pattern pattern = Pattern.compile(entry.getValue());
Matcher matcher = pattern.matcher(body);
if (matcher.find()) {
result.append("(不合法标签内的内容:").append(matcher.group(0)).append(");");
}
}
return result.toString();
}
}
以上就是大致实现思路。
测试字符串:
运行截图
因篇幅问题不能全部显示,请点此查看更多更全内容
怀疑对方AI换脸可以让对方摁鼻子 真人摁下去鼻子会变形
女子野生动物园下车狼悄悄靠近 后车司机按喇叭提醒
睡前玩8分钟手机身体兴奋1小时 还可能让你“变丑”
惊蛰为啥吃梨?倒春寒来不来就看惊蛰
男子高速犯困开智能驾驶出事故 60万刚买的奔驰严重损毁