SpringBoot 拦截器 统一日志 记录用户请求返回日志
你请求我接口,传了什么参数,我返回了什么值给你,全部记下来。防止扯皮
需求:记录每次用户请求Controller的Body参数,
思路:在每个Controller 该当中记录,容易漏记,如果在拦截器里面记的话,可以统一处理
问题:在postHandle 里面记,request.getInputStream() 取出来是空的,放在preHandle里面,就进不到 Controller 里面了。报:I/O error while reading input message; nested exception is java.io.IOException: Stream closed
原因:在拦截器已经读取了请求体中的内容,这时候请求的流中已经没有了数据,就是说HttpServletRequest请求体中的内容一旦读取就不不存在了,所以直接读取是不行的。
方案:对httprequest进行修饰,自定义的包装类来实现
添加 RequestLogFilter
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; @Component
@WebFilter(filterName = "requestLogFilter",urlPatterns = {"/*"},
initParams = {@WebInitParam(name = "ignoredUrl", value = ".css;.js;.jpg;.png;.gif;.ico;.html"),
@WebInitParam(name = "filterPath", value = "/user/login#/user/registerUser")})
public class RequestLogFilter implements OncePerRequestFilter { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override
protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
boolean writeLog = true;
String ignoreLog = "swagger;upload;.jpg;.png;.zip;.dat;.ico;.pdf;download"; //可以放配置文件
RequestLogWrapper requestWapper = null;
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
for (String item : ignoreLog.split(";")) {
if ("/".equals(request.getRequestURI()) || request.getRequestURI().toLowerCase().contains(item)) {
//有一个包含就不记
writeLog = false;
break;
}
}
if (writeLog) {
requestWapper = new RequestLogWrapper(request);
}
} ResponseLogWrapper responseLogWrapper = new ResponseLogWrapper(servletResponse); //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
// 在chain.doFiler方法中传递新的request对象
if (requestWapper == null) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
if (writeLog) {
filterChain.doFilter(requestWapper, responseLogWrapper);
} else {
filterChain.doFilter(requestWapper, servletResponse);
}
} if (writeLog) {
//打印返回响应日志
String result = new String(responseLogWrapper.getResponseData());
ServletOutputStream outputStream = servletResponse.getOutputStream();
outputStream.write(result.getBytes());
outputStream.flush();
outputStream.close(); String queryStr = StrUtil.isEmpty(servletRequest.getQueryString()) ? "" : "?" + servletRequest.getQueryString();
logger.info("Response => {}{} \r\n{}", servletRequest.getRequestURI(), queryStr, result);
}
} @Override
public void destroy() {
logger.info(">>>> RequestLogFilter destroy <<<<");
}
}
添加 RequestLogWapper
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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;
import java.nio.charset.StandardCharsets; public class RequestLogWrapper extends HttpServletRequestWrapper { private Logger logger = LoggerFactory.getLogger(this.getClass()); private String requestBody; public String getRequestBody() {
return requestBody;
} public RequestLogWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = getBodyString(request);
String queryStr = StrUtil.isEmpty(request.getQueryString()) ? "" : "?" + request.getQueryString(); String remoteAddr = request.getRemoteAddr();
Enumeration<String> headerNames = request.getHeaderNames();
String headerStr = "";
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
headerStr += StrUtil.format("{}:{};", headerName, headerValue);
}
logger.info("Request => {}{} RemoteAddr => {} \r\n Headers => {}\r\n{}", request.getRequestURI(), queryStr, remoteAddr, headerStr, requestBody);
} @Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
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(getInputStream()));
} public String getBodyString(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
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();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
ResponseLogWrapper
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*; public class ResponseLogWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null;//输出到byte array
private ServletOutputStream out = null;
private PrintWriter writer = null; public ResponseLogWrapper(HttpServletResponse resp) throws IOException {
super(resp);
buffer = new ByteArrayOutputStream();// 真正存储数据的流
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
} /** 重载父类获取outputstream的方法 */
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
} /** 重载父类获取writer的方法 */
@Override
public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
} /** 重载父类获取flushBuffer的方法 */
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
} @Override
public void reset() {
buffer.reset();
} /** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
} /** 内部类,对ServletOutputStream进行包装 */
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
bos = stream;
} @Override
public void write(int b) throws IOException {
bos.write(b);
} @Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
} @Override
public boolean isReady() {
return false;
} @Override
public void setWriteListener(WriteListener listener) { }
}
}
参考资源:
https://www.jianshu.com/p/ba2d5101ad90
https://blog.csdn.net/qq_38132283/article/details/107685797
SpringBoot 拦截器 统一日志 记录用户请求返回日志的更多相关文章
- SpringBoot 拦截器和自定义注解判断请求是否合法
应用场景举例: 当不同身份的用户请求一个接口时,用来校验用户某些身份,这样可以对单个字段数据进行精确权限控制,具体看代码注释 自定义注解 /** * 对比请求的用户身份是否符合 * @author l ...
- 分享知识-快乐自己:SpringBoot结合使用拦截器(判断是否用户是否已登陆)
所有的开发之中拦截器一定是一个必须要使用的功能,利用拦截器可以更加有效的实现数据的验证处理,而且最为幸运的是在SpringBoot之中所使用的拦截器与Spring中的拦截器完全一样. 基础拦截器操作: ...
- SpringBoot 拦截器获取http请求参数
SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 目录 SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 获取http请求参数是一种刚需 定义拦截器获取请求 为 ...
- Java结合SpringBoot拦截器实现简单的登录认证模块
Java结合SpringBoot拦截器实现简单的登录认证模块 之前在做项目时需要实现一个简单的登录认证的功能,就寻思着使用Spring Boot的拦截器来实现,在此记录一下我的整个实现过程,源码见文章 ...
- 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener
=================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...
- springboot + 拦截器 + 注解 实现自定义权限验证
springboot + 拦截器 + 注解 实现自定义权限验证最近用到一种前端模板技术:jtwig,在权限控制上没有用springSecurity.因此用拦截器和注解结合实现了权限控制. 1.1 定义 ...
- 分享知识-快乐自己:SpringMVC 结合使用拦截器(判断是否用户是否已登陆)
基础拦截器操作: 拦截器是一种AOP操作实现,那么在AOP之中用户一定不需要去关注拦截器的存在,用户只需要按照自己已经习惯的处理方式进行代码的编写即可. 首先我们先创建一个自定义的拦截器: packa ...
- Springboot 拦截器配置(登录拦截)
Springboot 拦截器配置(登录拦截) 注意这里环境为springboot为2.1版本 1.编写拦截器实现类,实现接口 HandlerInterceptor, 重写里面需要的三个比较常用的方 ...
- Springboot拦截器实现IP黑名单
Springboot拦截器实现IP黑名单 一·业务场景和需要实现的功能 以redis作为IP存储地址实现. 业务场景:针对秒杀活动或者常规电商业务场景等,防止恶意脚本不停的刷接口. 实现功能:写一个拦 ...
- SpringBoot拦截器及源码分析
1.拦截器是什么 java里的拦截器(Interceptor)是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止 ...
随机推荐
- logstash 配置文件语法介绍
大家好,我是蓝胖子,之前在构建服务监控实践那个系列里,有提到用logstash来做日志的收集,但是对于logstash的配置文件语法没有做很详细的介绍,今天就来详细聊聊logstash配置文件的语法. ...
- 一行代码解决IE停用后无法继续使用IE弹窗功能的问题
微软在2023年2月14日通过Edge浏览器更新,彻底封死IE.Windows Update中没有记录.开始菜单中的IE以及桌面IE图标双击自动打开Edge,默认程序设置了IE也没有任何效果,仅能通过 ...
- SpringBoot Seata 死锁问题排查
现象描述:Spring Boot项目,启动的时候卡住了,一直卡在那里不动,没有报错,也没有日志输出 但是,奇怪的是,本地可以正常启动 好吧,姑且先不深究为什么本地可以启动而部署到服务器上就无法启动的问 ...
- npm install 报-4048错误
报错原因: 有缓存 权限不够 有三种解决方法: 第一种:找到.npmrc文件并删除 在 C:\Users\自己用户的文件夹\ 下找到 .npmrc 文件并删除 注意:这个文件是隐藏的,需要显示隐藏才能 ...
- CH395实现主动ping对端功能(代码及说明)
目录 1.PING原理 1.1简介 1.2协议 1.3通信流程 2.代码解释 3.工程链接 PING原理 1.简介 PING是基于ICMP(Internet Control Message Proto ...
- auto{x}与auto(x)---一位中国小伙为cppreference作出的贡献
C++作为一门静态类型语言,是需要程序员声明变量类型的.然而来到了C++11,auto的诞生使得变量声明变得及为方便,尤其是对于比较长的模板类型,auto一定程度上为代码编写者减轻了负担.到了C+ ...
- 启动android项目时报dx.jar错误的解决方案
启动android项目时遇到如下错误: Failed to load E:\SDK\android-sdk-windows\build-tools\27.0.2\lib\dx.jar 原因: 自动使用 ...
- 面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
默认情况下,Spring Boot 中的 Bean 是非线程安全的.这是因为,默认情况下 Bean 的作用域是单例模式,那么此时,所有的请求都会共享同一个 Bean 实例,这意味着这个 Bean 实例 ...
- 在winform blazor hybrid中绘图
前几天跟大家介绍了在winform中使用blazor hybrid,而且还说配上blazor的ui可以让我们的winform程序设计的更加好看,接下来我想以一个在winform blazor hybr ...
- 2023-10-28:用go语言,给定一个n*m的二维矩阵,每个位置都是字符, U、D、L、R表示传送带的位置,会被传送到 : 上、下、左、右, . 、O分别表示空地、目标,一定只有一个目标点, 可以
2023-10-28:用go语言,给定一个n*m的二维矩阵,每个位置都是字符, U.D.L.R表示传送带的位置,会被传送到 : 上.下.左.右, . .O分别表示空地.目标,一定只有一个目标点, 可以 ...