Springboot如何利用拦截器拦截请求信息收集到日志详解
目录
- 1、需求
- 2、问题
- 2、获取
- 1)导入依赖为了获取客户端类型、操作系统类型、ip、port
- 2)封装获取body字符串的工具类
- 3)拦截器类
- 4)继承 HttpServletRequestWrapper类
- 5)过滤器类
- 6)拦截器过滤器配置类
- 总结
1、需求
最近在工作中遇到的一个需求,将请求中的客户端类型、操作系统类型、ip、port、请求方式、URI以及请求参数值收集到日志中,网上找资料说用拦截器拦截所有请求然后收集信息,于是就开始了操作:
2、问题
试了之后发现当请求方式为POST,前端发送数据json时只能用request.getReader()流获取,自信满满从流中获取之后发现请求之后报错:
getInputStream() has already been called for this request...
于是网上找答案,发现是ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。
然后又开始找解决方法,说既然ServletInputStream不支持重新读写,就把流读出来后用容器存储起来,后面就可以多次利用了。
于是继承 HttpServletRequestWrapper类(http请求包装器,其基于装饰者模式实现了HttpServletRequest界面)并实现想要重新定义的方法以达到包装原生HttpServletRequest对象。还需要在过滤器里将原生的HttpServletRequest对象替换成我们的RequestWrapper对象。
测试发现POST请求参数值可以在拦截器类中获取到了,本以为大功告成,又发现GET请求不好使了,开始报错Stream closed,一顿操作发现需要在过滤器进行判断,如果是POST请求走自己的继承的HttpServletRequestWrapper类请求,否则走普通的请求。终于成功!突然舒服了。
2、获取
1)导入依赖为了获取客户端类型、操作系统类型、ip、port
<dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUtils</artifactId> <version>1.21</version> </dependency>
2)封装获取body字符串的工具类
package com.btrc.access.util; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; public class RequestUtil { public static String getBodyString(HttpServletRequest request) { StringBuilder sb = new StringBuilder(); try ( InputStream inputStream = request.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))) ) { String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } }
3)拦截器类
package com.btrc.access.filter; import com.btrc.access.util.RequestUtil; import eu.bitwalker.useragentutils.UserAgent; import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 请求拦截器:拦截请求目的是将请求的信息收集到日志 */ public class RequestInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent")); //客户端类型 String clientType = userAgent.getOperatingSystem().getDeviceType().getName(); //客户端操作系统类型 String osType = userAgent.getOperatingSystem().getName(); //客户端ip String clientIp = request.getRemoteAddr(); //客户端port int clientPort = request.getRemotePort(); //请求方式 String requestMethod = request.getMethod(); //客户端请求URI String requestURI = request.getRequestURI(); //客户端请求参数值 String requestParam; //如果请求是POST获取body字符串,否则GET的话用request.getQueryString()获取参数值 if(StringUtils.equalsIgnoreCase(HttpMethod.POST.name(), requestMethod)){ requestParam = RequestUtil.getBodyString(request); }else{ requestParam = request.getQueryString(); } //客户端整体请求信息 StringBuilder clientInfo = new StringBuilder(); clientInfo.append("客户端信息:[类型:").append(clientType) .append(", 操作系统类型:").append(osType) .append(", ip:").append(clientIp) .append(", port:").append(clientPort) .append(", 请求方式:").append(requestMethod) .append(", URI:").append(requestURI) .append(", 请求参数值:").append(requestParam.replaceAll("\\s*", "")) .append("]"); //***这里的clientInfo就是所有信息了,请根据自己的日志框架进行收集*** System.out.println(clientInfo); //返回ture才会继续执行,否则一直拦截住 return true; } }
4)继承 HttpServletRequestWrapper类
package com.btrc.access.filter; import com.btrc.access.util.RequestUtil; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.nio.charset.Charset; public class AccessRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public AccessRequestWrapper(HttpServletRequest request) { super(request); body = RequestUtil.getBodyString(request).getBytes(Charset.forName("UTF-8")); } @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 int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
5)过滤器类
package com.btrc.access.filter; import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpMethod; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AccessFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; //如果是POST走自己的继承的HttpServletRequestWrapper类请求,否则走正常的请求 if(StringUtils.equalsIgnoreCase(HttpMethod.POST.name(), request.getMethod())){ //一定要在判断中new对象,否则还会出现Stream closed问题 filterChain.doFilter(new AccessRequestWrapper(request),servletResponse); }else{ filterChain.doFilter(servletRequest,servletResponse); } } @Override public void destroy() { } }
6)拦截器过滤器配置类
package com.btrc.access.config; import com.btrc.access.filter.AccessFilter; import com.btrc.access.filter.RequestInterceptor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.Filter; /** * 拦截器过滤器配置类 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean httpServletRequestReplacedFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new AccessFilter()); // /* 是全部的请求拦截,和Interceptor的拦截地址/**区别开 registration.addUrlPatterns("/*"); registration.setName("accessRequestFilter"); registration.setOrder(1); return registration; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**"); } }
总结
到此这篇关于Springboot如何利用拦截器拦截请求信息收集到日志的文章就介绍到这了,更多相关Springboot拦截请求信息到日志内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!