SpringBoot 之 SpringMVC拦截器从Request中获取参数并解决request的请求流只能读取一次的问题
为什么使用RequestBody只能读取一遍请求数据流?
那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()。
解决办法:重写HttpServletRequestWrapper方法
这种方法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request在填充进去,这样就可以多次读取request了
功能代码:
package com.digipower.erms.request.wrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
public class SQLInjectionHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bytes;
public SQLInjectionHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 读取输入流里的请求参数,并保存到bytes里
bytes = IOUtils.toByteArray(request.getInputStream());
}
public String getRequestBodyParame() {
return new String(bytes, Charset.forName("utf8"));
}
/**
*
* <p>
* Title: getInputStream
* </p>
* <p>
* Description:处理POST请求参数 RequestBody is missing 问题
* </p>
*
* @return
* @throws IOException
* @see javax.servlet.ServletRequestWrapper#getInputStream()
*/
@Override
public ServletInputStream getInputStream() throws IOException {
String body = new String(this.bytes);
return new BufferedServletInputStream(cleanXSS(body).getBytes());
}
class BufferedServletInputStream extends ServletInputStream {
private ByteArrayInputStream inputStream;
public BufferedServletInputStream(byte[] buffer) {
// 此处即赋能,可以详细查看ByteArrayInputStream的该构造函数;
this.inputStream = new ByteArrayInputStream(buffer);
}
@Override
public int available() throws IOException {
return inputStream.available();
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
@Override
public boolean isFinished() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setReadListener(ReadListener listener) {
// TODO Auto-generated method stub
}
}
/**
*
* <p>
* Title: getParameterValues
* </p>
* <p>
* Description: 解决html 和javascript 脚本注入问题
* </p>
*
* @param name
* @return
* @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
*/
@Override
public String[] getParameterValues(String parameter) {
// TODO Auto-generated method stub
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
@Override
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
return value;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return value;
}
}
2、过滤器SQLInjectionFilter
package com.digipower.erms.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import com.digipower.erms.request.wrapper.SQLInjectionHttpServletRequestWrapper;
/**
*
* @ClassName: SQLInjectionFilter
* @Description: ServletRequest 读取RequestBody is miss的问题
* @date: 2018年11月7日 上午9:03:25
*
*/
@WebFilter(filterName="SQLInjectionFilter",urlPatterns="/*")
public class SQLInjectionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
if(request instanceof HttpServletRequest) {
request = new SQLInjectionHttpServletRequestWrapper((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
springboot 配置Filter
package com.digipower;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
@ServletComponentScan("com.digipowert.erms.filter")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 注意这里要指向原先用main方法执行的Application启动类
return builder.sources(Application.class);
}
}
现在我们已经完成HttpServletRequest 转换为自定义SQLInjectionHttpServletRequestWrapper 包装对象。
下面说一下其应用场景:SQL 攻击注入、SpringAOP 参数日志记录。
SQL 攻击注入:SpringBoot 之 SpringMVC 实现SQL注入过滤 完整版
SpringAOP 参数日志记录:SpringBoot 之SpringAOP 请求日志记录功能(支持POST和GET)
上一篇: 详解CSS3阴影 box-shadow的使用和技巧总结
下一篇: php获取bing每日壁纸示例分享