当前位置 博文首页 > 文章内容

    Springboot如何利用拦截器拦截请求信息收集到日志详解

    作者:shunshunshun18 栏目:未分类 时间:2021-08-12 14:44:24

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    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("/**");
        }
    }
    

    总结