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

 更新时间:2021年08月12日 10:55:26   作者:Leil_blogs  
一些系统经常需要关注用户请求的具体信息,如用户信息、请求参数、响应结果等等,在SpringBoot应用中可通过拦截器的方式统一处理,下面这篇文章主要给大家介绍了关于Springboot如何利用拦截器拦截请求信息收集到日志的相关资料,需要的朋友可以参考下

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拦截请求信息到日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JVM工作原理和工作流程简述

    JVM工作原理和工作流程简述

    这篇文章主要介绍了关于JVM工作原理简述,主要弄清楚jvm运行的来龙去脉,感兴趣的可以一起来了解一下
    2020-07-07
  • Java中空指针异常该如何避免详解

    Java中空指针异常该如何避免详解

    空指针(Null Pointer Exception,NPE)是Java中最常见不过的异常了,其原因虽然显而易见,但是开发人员往往会忽略,或未能及时采取措施,下面这篇文章主要给大家介绍了关于Java中空指针异常该如何避免的相关资料,需要的朋友可以参考下
    2022-03-03
  • Jenkins自动部署Net Core过程图解

    Jenkins自动部署Net Core过程图解

    这篇文章主要介绍了Jenkins自动部署Net Core过程图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Spring Boot 与 mybatis配置方法

    Spring Boot 与 mybatis配置方法

    这篇文章主要介绍了Spring Boot 与 mybatis配置方法,需要的朋友可以参考下
    2017-06-06
  • MyBatis在SQL语句中如何获取list的大小

    MyBatis在SQL语句中如何获取list的大小

    这篇文章主要介绍了MyBatis在SQL语句中如何获取list的大小问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • SpringBoot2.0如何自定义404页面渲染

    SpringBoot2.0如何自定义404页面渲染

    这篇文章主要介绍了SpringBoot2.0如何自定义404页面渲染问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java如何获取当前进程ID以及所有Java进程的进程ID

    Java如何获取当前进程ID以及所有Java进程的进程ID

    本篇文章主要介绍了Java如何获取当前进程ID以及所有Java进程的进程ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 几句话说清session,cookie和token的区别及说明

    几句话说清session,cookie和token的区别及说明

    这篇文章主要介绍了几句话说清session,cookie和token的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • mybatis中批量更新多个字段的2种实现方法

    mybatis中批量更新多个字段的2种实现方法

    当我们使用mybatis的时候,可能经常会碰到一批数据的批量更新问题,因为如果一条数据一更新,那每一条数据就需要涉及到一次数据库的操作,本文主要介绍了mybatis中批量更新多个字段的2种实现方法,感兴趣的可以了解一下
    2023-09-09
  • java对于目录下文件的单词查找操作代码实现

    java对于目录下文件的单词查找操作代码实现

    这篇文章主要介绍了java对于目录下文件的单词查找操作代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11

最新评论