web篇过滤器拦截器监听器
Posted 小崔编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web篇过滤器拦截器监听器相关的知识,希望对你有一定的参考价值。
项目中之前也都用过这些东西,现在写出来,一是记录在项目中的使用,二是全面认识一下三大利器。思来想去,我认为这三大利器总的目的是为了解耦,让代码更加简洁可读,所以问题来了,它是怎样实现的呢?这种方式是不是我也可以用到我自己编码的过程中呢?这都要自己一点点去探索,可能这篇不能马上解决这个问题,但在后续的文章会慢慢解读。
目录
一、过滤器和拦截器的区别
- 使用范围不一样。过滤器是作用在servlet容器中的,而且拦截器是spring组件,作用在spring中的。
- 使用的资源不一样。由于第一点,过滤器只能涉及到request/repnose等;而拦截器归spring管理,spring中的配置、bean等都可以使用。
- 深度不一样。过滤器只在servlet前后起作用;而过滤器可以在方法前后、异常抛出前后等,拦截器有更大的灵活性,故在spring架构中,建议优先使用拦截器
二、监听器
监听器的作用是监听一些事件的发生从而进行一些操作,比如监听ServletContext,HttpSession的创建,销毁,从而执行一些初始化加载配置文件的操作,当Web容器启动后,Spring的监听器会启动监听,监听是否创建ServletContext的对象,如果发生了创建ServletContext对象这个事件(当web容器启动后一定会生成一个ServletContext对象,所以监听事件一定会发生),ContextLoaderListener类会实例化并且执行初始化方法,将spring的配置文件中配置的bean注册到Spring容器中
常用的监听器:
1.ServletContextListener -- 监听servletContext对象的创建以及销毁
1.1 contextInitialized(ServletContextEvent arg0) -- 创建时执行
1.2 contextDestroyed(ServletContextEvent arg0) -- 销毁时执行
2.HttpSessionListener -- 监听session对象的创建以及销毁
2.2 sessionCreated(HttpSessionEvent se) -- 创建时执行
2.2 sessionDestroyed(HttpSessionEvent se) -- 销毁时执行
3.ServletRequestListener -- 监听request对象的创建以及销毁
3.1 requestInitialized(ServletRequestEvent sre) -- 创建时执行
3.2 requestDestroyed(ServletRequestEvent sre) -- 销毁时执行
4.ServletContextAttributeListener -- 监听servletContext对象中属性的改变
4.1 attributeAdded(ServletContextAttributeEvent event) -- 添加属性时执行
4.2 attributeReplaced(ServletContextAttributeEvent event) -- 修改属性时执行
4.3 attributeRemoved(ServletContextAttributeEvent event) -- 删除属性时执行
5.HttpSessionAttributeListener --监听session对象中属性的改变
5.1 attributeAdded(HttpSessionBindingEvent event) -- 添加属性时执行
5.2 attributeReplaced(HttpSessionBindingEvent event) -- 修改属性时执行
5.3 attributeRemoved(HttpSessionBindingEvent event) -- 删除属性时执行
6.ServletRequestAttributeListener --监听request对象中属性的改变
6.1 attributeAdded(ServletRequestAttributeEvent srae) -- 添加属性时执行
6.2 attributeReplaced(ServletRequestAttributeEvent srae) -- 修改属性时执行
6.3 attributeRemoved(ServletRequestAttributeEvent srae) -- 删除属性时执行
7.自定义监听器
三、使用场景
拦截器:
参数校验(sql注入检验)、做xss攻击校验(两种方式,一种是流,一种是直接获取),做校验签名,做鉴权,请求计数(限流)等。
这里遇到的问题是做xss攻击时,两种方式
第一种是:有的controller方法是直接从request中获取的此参数
public class XssFilter implements Filter
@Override
public void destroy()
// TODO Auto-generated method stub
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
XSSRequestWrapper xssRequest = new XSSRequestWrapper ((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
@Override
public void init(FilterConfig arg0) throws ServletException
// TODO Auto-generated method stub
public class XSSRequestWrapper extends HttpServletRequestWrapper
/**
* 构造方法
*
* @param request
*/
public XSSRequestWrapper(HttpServletRequest request)
super(request);
/**
* 处理参数值
*/
@Override
public String[] getParameterValues(String parameter)
String[] values = super.getParameterValues(parameter);
if (values == null)
return null;
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++)
encodedValues[i] = dealString(values[i]);
return encodedValues;
/**
* 覆盖
*
* @param value
* @return
* @date 上午9:30:43
* @author DuChaoWei
* @descripte
*/
@Override
public String getParameter(String parameter)
String value = super.getParameter(parameter);
return dealString(value);
@Override
public String getHeader(String name)
String value = super.getHeader(name);
return dealString(value);
/**
* 字符串处理
*
* @param value
* @return
* @date 上午9:30:43
* @author DuChaoWei
* @descripte
*/
private String dealString(String value)
if (value != null)
// 采用spring的StringEscapeUtils工具类 实现
StringEscapeUtils.escapehtml(value);
StringEscapeUtils.escapejavascript(value);
StringEscapeUtils.escapeSql(value);
return value;
第二种是:有的controller方法是通过注解@ReuqestBody转换成实体对象获取的参数
public class CheckSQLInjectionFilter implements Filter
private List<String> excludes = new ArrayList<>();
public void setExcludes(List<String> excludes)
this.excludes = excludes;
public List<String> getExcludes()
return excludes;
@Override
public void init(FilterConfig filterConfig) throws ServletException
String excludes = filterConfig.getInitParameter("excludes");
if (StringUtil.isNotBlank(excludes))
String[] array = excludes.split(",");
for (String url : array)
this.excludes.add(url);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
String requestPath = req.getRequestURI();
requestPath = requestPath.substring(req.getContextPath().length() + 1);
while (requestPath.endsWith("/")) //预防uri末尾有 ‘/’
requestPath = requestPath.substring(0, requestPath.length()-1);
for (String str : excludes)
if (str.endsWith("*"))
if (requestPath.startsWith(str.substring(0, str.length() - 1)))
chain.doFilter(req, resp);
return;
if(str.equals(requestPath))
chain.doFilter(req, resp);
return;
Map<String, Object> paramMap = new HashMap<>();
String type = req.getContentType();
ServletRequest requestWrapper = null;
if(req instanceof HttpServletRequest)
requestWrapper = new ReaderReuseHttpServletRequestWrapper(req);
Reader reader = requestWrapper.getReader();
// 读取Request Payload数据
String Payload = IOUtils.toString(reader);
if (type != null && type.startsWith("application/json"))
JSONObject jsonObject = JSONObject.parseObject(Payload);
if (jsonObject != null)
for(Map.Entry<String, Object> entry : jsonObject.entrySet())
paramMap.put(entry.getKey(), entry.getValue());
else if(type != null && type.startsWith("text/plain"))
String[] kvs = Payload.split("&");
for (String kv : kvs)
String[] lf = kv.split("=");
paramMap.put(lf[0], lf[1]);
// 获取请求参数
Enumeration en = req.getParameterNames();
while(en.hasMoreElements())
String name = (String) en.nextElement();
String value = req.getParameter(name);
paramMap.put(name, value);
for(Map.Entry<String, Object> node : paramMap.entrySet())
boolean valid = true;
if (node.getValue() instanceof String)
valid = CheckSQLInjectionUtil.validate((String)node.getValue());
if (!valid)
resp.setContentType("application/json;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("\\"success\\":false,\\"msg\\":\\""+HttpStatus.SECURITY.getName()+"\\",\\"code\\":"+HttpStatus.SECURITY.getCode()+"");
writer.flush();
return;
chain.doFilter(requestWrapper, resp);
@Override
public void destroy()
/**
* 两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,
* 那么流读了一次就没有了,所以只能被调用一次。
* 既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了
* @author LIU
*
*/
public static class ReaderReuseHttpServletRequestWrapper extends HttpServletRequestWrapper
private final byte[] body;
public ReaderReuseHttpServletRequestWrapper(HttpServletRequest request)
throws IOException
super(request);
body = IOUtils.toString(request.getReader()).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();
;
类似于上段代码,可以从流中获取参数,在一一校验。当然还有其他好多方法使用。
拦截器
从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
比如:只有登录的用户才能有权限进入某些菜单。
监听器
https://www.jianshu.com/p/95f2ed98aaad
四、总结
主要思想是尽可能的优化代码,使代码更加优雅,感觉这个很有趣,大家有好多的想法和思路,比如lombok是怎样简化代码的,深入其中,自得乐趣。
以上是关于web篇过滤器拦截器监听器的主要内容,如果未能解决你的问题,请参考以下文章