Filter 记录日志(Get Post 参数)
Posted 北冥鱼_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Filter 记录日志(Get Post 参数)相关的知识,希望对你有一定的参考价值。
一、拦截器介绍
拦截器(Interceptor)介绍
拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现(AOP切面编程只是一种编程思想而已)。
你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置……
在 Spring 中,当请求发送到 Controller 时,在被 Controller 处理之前,它必须经过 Interceptors(0或多个)。
Spring Interceptor 是一个非常类似于Servlet Filter 的概念 。
Interceptor 作用
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;
- 权限检查:如登录检测,进入处理器检测是否登录;
- 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
- 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。
自定义 Interceptor
自定义 Interceptor ,需要实现 org.springframework.web.servlet.HandlerInterceptor接口或继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter类,并且重写以下 3 个方法:
- preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) 方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。
- postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。
- afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。
过滤器 Filter 介绍
Filter 介绍
Filter 是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Java中的 Filter 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。
过滤链的好处是,执行过程中任何时候都可以被打断,只要不执行chain.doFilter()就不会再执行后面的请求内容。实际使用时,需要特别注意过滤器的执行顺序:
Filter 作用
- 在 HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest
- 根据需要检查 HttpServletRequest ,可以修改HttpServletRequest 头和数据
- 在 HttpServletResponse 到达客户端之前,拦截HttpServletResponse
- 根据需要检查 HttpServletResponse ,可以修改HttpServletResponse 头和数据
自定义 Filter
所有的 Servlet 过滤器类都必须实现 javax.servlet.Filter 接口
Interceptor 、Filter 区别
-
Filter 流程是线性的, url 传来之后,检查之后,可保持原来的流程继续向下执行,被下一个 Filter, servle t接收等,而servlet 处理之后,不会继续向下传递。Filter 功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
Filter 可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等 -
interceptor 拦截器,类似于filter,不过在struts.xml中配置,不是在 web.xml,并且不是针对 URL 的,而是针对 action,当页面提交 action 时,进行过滤操作,相当于 struts1.x 提供的 plug-in 机制,可以看作,前者是 struts1.x 自带的filter,而 interceptor 是 struts2 提供的 filter.
与 filter 不同点:
1). 不在 web.xml中 配置,而是在 struts.xml 中完成配置,与action 在一起
2).可由 action 自己指定用哪个interceptor 来在接收之前做事
为什么使用 Filter 而不是拦截器
这是因为在 Filter 中获取网络请求的入参、出参更方便:
通过重写 HttpServletRequestWrapper 类 获 getInputStream中的流数据,然后在将body数据进行重新写入传递下去。
-
GET 传递
参数可以直接通过request.getParameter获取。 -
Post 传递
因为(request.getInputStream()只能够读取一次),post参数不能直接从request.getInputStream() 读取,必须要进行「response包装」重新写。
一个实际例子
1、request 参数包装:
MyRequestWrapper.java:
package com.ruoyi.business.Log;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* @description: MyRequestWrapper
* @author: chang
* @create: 2019-07-24 11:15
**/
@Slf4j
public class MyRequestWrapper extends HttpServletRequestWrapper
private Map<String, String[]> params = new HashMap<>();
private String body;
private byte[] buffer;
/**
* Constructs a request object wrapping the given request.
*
* @param request
* @throws IllegalArgumentException if the request is null
*/
public MyRequestWrapper(HttpServletRequest request) throws IOException
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try
InputStream inputStream = request.getInputStream();
if (inputStream != null)
bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0)
stringBuilder.append(charBuffer, 0, bytesRead);
else
stringBuilder.append("");
catch (IOException ex)
throw ex;
finally
if (bufferedReader != null)
try
bufferedReader.close();
catch (IOException ex)
throw ex;
body = stringBuilder.toString();
public String getBody()
return this.body;
public ServletInputStream getInputStream()
return new BufferedServletInputStream(this.buffer);
/**
* 重载构造方法
*/
public MyRequestWrapper(HttpServletRequest request, Map<String, Object> extendParams) throws IOException
this (request);
//这里将扩展参数写入参数表
addAllParameters(extendParams);
/**
* 重写getParameter方法
*
* @param name 参数名
* @return 返回参数值
*/
@Override
public String getParameter(String name)
String[] values = params.get(name);
if (values == null || values.length == 0)
return null;
return values[0];
@Override
public String[] getParameterValues(String name)
String[] values = params.get(name);
if (values == null || values.length == 0)
return null;
return values;
/**
* 增加多个参数
*
* @param otherParams 增加的多个参数
*/
public void addAllParameters(Map<String, Object> otherParams)
for (Map.Entry<String, Object> entry : otherParams.entrySet())
addParameter(entry.getKey(), entry.getValue());
/**
* 将parameter的值去除空格后重写回去
*/
public void modifyParameterValues()
Set<String> set = params.keySet();
Iterator<String> it = set.iterator();
while (it.hasNext())
String key = (String) it.next();
String[] values = params.get(key);
values[0] = values[0].trim();
params.put(key, values);
/**
* 增加参数
*
* @param name 参数名
* @param value 参数值
*/
public void addParameter(String name, Object value)
if (value != null)
if (value instanceof String[])
params.put(name, (String[]) value);
else if (value instanceof String)
params.put(name, new String[](String) value);
else
params.put(name, new String[]String.valueOf(value));
2、response参数包装:
HttpServletResponseWrapper.java:
package com.ruoyi.business.Log;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @description: 返回值输出代理类
* @author: chang
* @create: 2019-06-12 09:28
**/
public class EcaResponseWrapper extends HttpServletResponseWrapper
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
public EcaResponseWrapper(HttpServletResponse response)
super(response);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
@Override
public ServletOutputStream getOutputStream()
throws IOException
return out;
@Override
public void flushBuffer()
throws IOException
if (out != null)
out.flush();
public byte[] getContent()
throws IOException
flushBuffer();
return buffer.toByteArray();
class WrapperOutputStream extends ServletOutputStream
private ByteArrayOutputStream bos;
public WrapperOutputStream(ByteArrayOutputStream bos)
this.bos = bos;
@Override
public void write(int b)
throws IOException
bos.write(b);
@Override
public boolean isReady()
// TODO Auto-generated method stub
return false;
@Override
public void setWriteListener(WriteListener arg0)
// TODO Auto-generated method stub
3、辅助参数类:
BufferedServletInputStream.java
package com.ruoyi.business.Log;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @description: 自定义输入流
* @author: chang
* @create: 2021-10-27 10:27
**/
public class BufferedServletInputStream extends ServletInputStream
private ByteArrayInputStream sourceStream;
private boolean finished = false;
public BufferedServletInputStream(byte[] buffer)
this.sourceStream = new ByteArrayInputStream(buffer);
public final InputStream getSourceStream()
return this.sourceStream;
@Override
public int read() throws IOException
int data = this.sourceStream.read();
if (data==-1)
this.finished = true;
return data;
@Override
public int available()
return this.sourceStream.available();
@Override
public void close() throws IOException
super.close();
this.sourceStream.close();
@Override
public boolean isFinished()
return this.finished;
@Override
public boolean isReady()
return true;
@Override
public void setReadListener(ReadListener readListener)
throw new UnsupportedOperationException();
4、过滤器Filter:
EcaResponseFilter.java
package com.ruoyi.business.Log;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.ruoyi.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.TreeMap;
/**
* @description: 返回值输出过滤器,修改返回值加密方式
* @author: chang
* @create: 2019-06-12 09:38
**/
@WebFilter(urlPatterns = "/*", filterName = "EcaResponseFilter")
public class EcaResponseFilter implements Filter
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final Gson gson = new Gson();
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException
HttpServletRequest req = (HttpServletRequest) request;
String requestUri = req.getRequestURI();
// 过滤不需记录日志的链接
if (requestUri.contains(".js")||requestUri.contains(".css")||requestUri.contains(".ico")||requestUri.contains("/fonts/"))
filterChain.doFilter(request, response);
else
StringBuilder parameterSb = new StringBuilder(2048);
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements())
String key = parameterNames.nextElement();
String value = request.getParameter(key);
parameterSb.append(key+"="+value+", ");
String sb = parameterSb.toString();
if (StringUtils.isNotEmpty(sb))
logger.info("responseFlow requestUri == "+requestUri+" 在filter中打印的 get 请求参数:"+ "\\n" +""+sb+"");
// 处理请求流程
String result = requestFlow(request, response, filterChain);
// 处理返回流程
responseFlow(request, response, result);
@Override
public void init(FilterConfig arg0)
throws ServletException
@Override
public void destroy()
public String requestFlow(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException
HttpServletRequest req = (HttpServletRequest) request;
String requestUri = req.getRequestURI();
MyRequestWrapper requestWrapper = new MyRequestWrapper(req);
/**
* 获取 POST 请求参数
*/
TreeMap paramsMaps = new TreeMap();
if ("POST".equals(req.getMethod().toUpperCase()))
String body = requestWrapper.getBody();
if (StringUtils.isNotEmpty(body))
try
paramsMaps = JSONObject.parseObject(body, TreeMap.class);
logger.info("responseFlow requestUri == "+requestUri+" 在filter中打印的 post 请求参数:"+ "\\n" +paramsMaps.toString());
catch (Exception e)
logger.error("JSONObject.parseObject error: requestUri= "+requestUri);
EcaResponseWrapper wrapper = new EcaResponseWrapper((HttpServletResponse) response);
// 这句话非常重要,注意看到第二个参数是我们的包装器而不是response
try
long bgn = System.currentTimeMillis();
filterChain.doFilter(requestWrapper, wrapper);
long end = System.currentTimeMillis();
try
byte[] content = wrapper.getContent();//获取返回值
//判断是否有值
if (content.length > 0)
String result = new String(content, "UTF-8");
logger.info("responseFlow requestUri == "+requestUri+" 在filter中打印的返回参数 result :"+ "\\n" +result);
logger.info("filter中记录 "+(requestUri)+" 耗时:"+(end-bgn)+"ms");
return result;
catch (Exception e)
String stackStr = toStackTrace(e);
logger.info("wrapper.getContent() 获取返回值 error: "+stackStr);
catch (Exception e)
String stackStr = toStackTrace(e);
logger.info("requestFlow Exception: "+stackStr);
return "";
public void responseFlow(ServletRequest request, ServletResponse response, String result) throws IOException
String responseStr = result;
// 输出最终的结果 --> 做完处理之后再把这个值返回回去
response.getOutputStream().write(responseStr.getBytes());
/**
* 获取堆栈信息
* @param e
* @return
*/
public String toStackTrace(Exception e)
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try
e.printStackTrace(pw);
String result = sw.toString().substring(0,1600);
return result;
catch (Exception var4)
return " var4";
5、过滤器嵌入项目配置:
MyAdapter.java
package com.ruoyi.business.Log;
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;
@Configuration
public class MyAdapter implements WebMvcConfigurer
@Bean
public LogInterceptor customInterceptor()
return new LogInterceptor();
/**
* Description:重写增加自定义拦截器的注册,某一个拦截器需要先注册进来,才能工作
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
// 添加拦截器
// registry.addInterceptor(customInterceptor()).addPathPatterns("/**");
@Bean
public FilterRegistrationBean someFilterRegistration()
FilterRegistrationBean registration = new FilterRegistrationBean();
// 配置一个返回值加密过滤器
registration.setFilter(new EcaResponseFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("responseFilter");
return registration;
参考链接:
以上是关于Filter 记录日志(Get Post 参数)的主要内容,如果未能解决你的问题,请参考以下文章