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
eu.bitwalker UserAgentUtils1.21
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 {@Overridepublic 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(); //客户端ipString clientIp = request.getRemoteAddr(); //客户端portint clientPort = request.getRemotePort(); //请求方式String requestMethod = request.getMethod(); //客户端请求URIString 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")); }@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream())); }@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read(); }@Overridepublic boolean isFinished() {return false; }@Overridepublic boolean isReady() {return false; }@Overridepublic 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic 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); }}@Overridepublic 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; /** * 拦截器过滤器配置类 */@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {@Beanpublic FilterRegistrationBean httpServletRequestReplacedFilter() {FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new AccessFilter()); // /* 是全部的请求拦截,和Interceptor的拦截地址/**区别开registration.addUrlPatterns("/*"); registration.setName("accessRequestFilter"); registration.setOrder(1); return registration; }@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**"); }}
总结 【Springboot如何利用拦截器拦截请求信息收集到日志详解】到此这篇关于Springboot如何利用拦截器拦截请求信息收集到日志的文章就介绍到这了,更多相关Springboot拦截请求信息到日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 如何寻找情感问答App的分析切入点
- Activiti(一)SpringBoot2集成Activiti6
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus使用queryWrapper如何实现复杂查询
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 如何在Mac中的文件选择框中打开系统隐藏文件夹
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- java中如何实现重建二叉树