背景:
1、SpringBoot
下获取Request
,打印日志或参数校验。
2、Request
请求流不能重复读取。 java.lang.IllegalStateException: getReader() has already been called for this request
异常。
3、拦截时对于MultipartHttpServletRequest
不能转换成自定义RequestWrapper
。
实现:
1、实现一个过滤器,对于所有请求路径。
2、对于文件请求类型multipart/
不进行自定义处理
3、自定义RequestWrapper
处理流,然后通过FilterChain
传下去
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
@Order(1)//执行顺序的优先级
@Component
@WebFilter(filterName = "httpServletRequestFilter", urlPatterns = "/*") //过滤所有路径
public class RequestReplacedFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//文件上传类型不处理
if (Objects.isNull(request) || Optional.ofNullable(request.getContentType()).orElse("").startsWith("multipart/")) {
chain.doFilter(request, response);
return;
}
//自定义RequestWrapper处理流,然后通过FilterChain传下去
chain.doFilter( new RequestWrapper((HttpServletRequest) request), response);
}
}
4、自定义RequestWrapper
处理流,可重复读取inputStream
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = 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 byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
自定义拦截器,开始和结束时记录日志。
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
@Component
@Slf4j
public class AccessLogInterceptor implements HandlerInterceptor {
private static final String LOGGER_SEND_TIME = "SEND_TIME";
/**
* 在请求开始记录日志实体
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.setAttribute(LOGGER_SEND_TIME, System.currentTimeMillis());
// 打印请求内容
log.info("Request URL :{}", request.getRequestURI());
log.info("Request Method :{}", request.getMethod());
log.info("Request IP :{}", AddressIpUtils.getIpAddress(request));
if (request instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
Map<String, String[]> parameterMap = multipartHttpServletRequest.getParameterMap();
for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
log.info("Request Param :{}", stringEntry.getKey() + "=" + Arrays.toString(stringEntry.getValue()));
}
Map<String, MultipartFile> fileMap = multipartHttpServletRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
MultipartFile file = entry.getValue();
String fileName = file.getOriginalFilename();
log.info("Request Param :{}", fileName);
}
} else if (request instanceof RequestWrapper) {
RequestWrapper requestWrapper = (RequestWrapper) request;
log.info("Request Param :{}", RequestUtil.getParameters(requestWrapper));
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long current = System.currentTimeMillis();
long sendTime = (long) request.getAttribute(LOGGER_SEND_TIME);
log.info("Duration :{}ms", current - sendTime);
log.info("CreateTime :{}", LocalDateTime.now());
log.info("Http Status :{}", response.getStatus());
}
}
获取请求参数工具类
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class RequestUtil {
public static String getParameters(RequestWrapper request) throws JsonProcessingException {
if (StrUtil.isNotBlank(request.getBody())) return request.getBody();
ObjectMapper mapper = new ObjectMapper();
Enumeration<String> enume = request.getParameterNames();
Map<String, Object> map = new HashMap<String, Object>();
while (enume.hasMoreElements()) {
String key = enume.nextElement();
String value = request.getParameter(key);
map.put(key, value);
}
return mapper.writeValueAsString(map);
}
}
import javax.servlet.http.HttpServletRequest;
/**
* 获取ip访问地址工具类
*/
public class AddressIpUtils {
public static String getIpAddress(HttpServletRequest request) {
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = XFor.indexOf(",");
if(index != -1){
return XFor.substring(0,index);
}else{
return XFor;
}
}
XFor = Xip;
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
return XFor;
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
}
拦截器注册
@Configuration
public class WebAppConfiguration implements WebMvcConfigurer {
@Autowired
AccessLogInterceptor accessLogInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(accessLogInterceptor).addPathPatterns("/**");
}
}