一、前言
在请求分发给具体的处理器之前,我们可以通过过滤器和拦截器来截取请求信息,但是藏于body中的参数确不能直接获取,因为在流对象中只允许一次性打开,这就好比你将桶里的水倒干净了,然后把桶交给别人。
常规的处理方案有两种:
1、将流内容导入另一个流,然后将新的流传递
2、通过mark()和reset()方法将流内容回流。
最初,我采用第二种方式,却发现依旧存在很多问题,比如mark长度难以确定,无法绕开servletRequest无法二次打开的设定。于是采用第一种办法。
二、如何复制请求流
package com.baye.common.core.filter;
import org.apache.commons.lang.StringUtils;
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.Charset;
/**
* 复制请求,代替原有request传递给具体处理器
*
* @author sunyiran
* @date 2018-11-27
*/
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 利用body存储流内容
*/
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = readBytes(request.getReader());
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
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() throws IOException {
return bais.read();
}
};
}
/**
* 通过BufferedReader和字符编码集转换成byte数组
*
* @param br
* @return
* @throws IOException
*/
private byte[] readBytes(BufferedReader br ) throws IOException {
String str;
StringBuilder retStr = new StringBuilder();
while ((str = br.readLine()) != null) {
retStr.append(str);
}
if (StringUtils.isNotBlank(retStr.toString())) {
return retStr.toString().getBytes(Charset.forName("utf-8"));
}
return null;
}
}
三、如何过滤请求
注意在主类上添加:@ServletComponentScan
package com.baye.common.core.filter;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
/**
* todo 上线时关闭
* @author sunyiran
* @date 2018-11-27
*/
@WebFilter(urlPatterns = "/*")
public class RequestLogFilter implements Filter{
private static final Log logger = LogFactory.getLog("RequestLogdFilter");
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ServletRequest requestWrapper = printRequestLog((HttpServletRequest) request);
if (requestWrapper == null) {
chain.doFilter(request,response);
}
chain.doFilter(requestWrapper, response);
}
@Override
public void init(FilterConfig arg0) {
}
/**
* 打印请求详情
* @param request
* @return
* @throws IOException
*/
private ServletRequest printRequestLog(HttpServletRequest request) throws IOException {
Map<String, String[]> params = request.getParameterMap();
String requestUri = request.getRequestURI();
String ip = getRemoteHost(request);
StringBuilder msg = new StringBuilder(
"\r\n" + ip + "-请求:" + requestUri + "-->[" + request.getMethod() + "]\r\n");
Enumeration<String> headers = request.getHeaderNames();
String key = null;
while (headers.hasMoreElements()) {
key = headers.nextElement();
msg.append("\t").append(key).append(":\t\t").append(request.getHeader(key)).append("\r\n");
}
msg.append("---------------------------------------------------------------------------------\r\n");
//如果传递方式为RequestParam
if (params != null && !params.isEmpty()) {
msg.append("\tRequestParam:");
for (Map.Entry<String, String[]> item : params.entrySet()) {
msg.append("\t").append(item.getKey()).append(":\t\t").append(StringUtils.join(item.getValue(), "/"))
.append("\r\n");
}
logger.info(msg.toString());
return request;
}
//如果传递方式为RequestBody
msg.append("\r\n\tRequestBody:");
if (request.getContentLength() > 0) {
request = new BodyReaderHttpServletRequestWrapper(request);
}else {
logger.info(msg.toString());
return request;
}
BufferedReader br = request.getReader();
String str ;
StringBuilder retStr= new StringBuilder();
while ((str = br.readLine()) != null) {
retStr.append(str);
}
msg.append(retStr);
logger.info(msg.toString());
return request;
}
/**
* 获取请求主机地址
* @param request
* @return
*/
public String getRemoteHost(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}