在很多Web项目中,都会用到过滤器( Filter ),如参数过滤、防止SQL注入、防止页面攻击、空参数矫正、Token 验证、Session验证、点击率统计等。
认识过滤器
为什么要使用过滤器
在Web开发中,常常会有这样的需求:在所有接口中去除用户输入的非法字符,以防止引起业务异常。要实现这个功能,可以有很多方法,如:
- 在前端参数传入时进行校验,先过滤掉非法字符,然后,返回用户界面提示用户重新输入。
- 后端接收前端没有过滤的数据,然后过滤非法字符。
- 利用 Fiter 处理项目中所有非法字符。
很明显前两种实现方法会存在重复代码,因为每个前端页面或后端都需要处理,这样会导致代码极难维护。如果用过滤器来实现, 则只需要用过滤器对所有接口进行过滤处理。这样非常方便,同时不会出现冗余代码。
使用 Filter 的步骤
- 新建类,实现Filer抽象类。
- 重写init、doFilter、 destroy 方法。
- 在Spring Boot入口中添加注解 @ServletComponentScan,以注册 Filter
在重写3个方法后,还可以进一步修改 request 参数使用的封装方式,如:
- 编写 ParameterRequestWrapper 类继承 HttpServletRequestWrapper 类。
- 编写 ParameterRequestWrapper 类构造器。
- 在构造器中覆写父类构造器,并将 request.getParameterMap 加入子类的成员变量。
- 編写 addParam 方法。
- 修改参数并调用 ParamelerRequstWrapper 实例,并保存params
- 调用 doFilter 方法中的 FiteChain 变量,以重新封装修改后的 request。
编写过滤器类
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//作用范围
@WebFilter(urlPatterns = "/*")
public class FilterDemo01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//init逻辑,该init将在服务器启动时调用
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//request处理逻辑
//request在封装逻辑
//chain重新写回request和response
System.out.println("过滤器");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
//写destroy逻辑,该destroy逻辑将在服务器关闭时调用
}
}
在入口类中开启 Servlet 支持
直接在入口类加入 @ServletComponentScan 即可。因为通过注解 @WebFilter(urlPatterns = "/*") 定义了 urlPatterns 的变量值为“*”,代表“所有的路径”。
测试
@RestController
@RequestMapping("test")
public class TestController {
/**
* 测试get方法
* @param userName
* @return
*/
@RequestMapping(value="get",method = RequestMethod.GET)
public String testGet(String userName){
System.out.println("get使用参数:"+userName);
return userName;
}
/**
* 测试post方法
* @param userName
* @return
*/
@RequestMapping(value="post",method = RequestMethod.POST)
public String testPost(String userName){
System.out.println("post使用参数:"+userName);
return userName;
}
}
所以,用户在访问本项目下的任何路径的页面时,此过滤器都会在控制台输出以下信息:
认识监听器
监听器(Listener)用于监听 Web 应用程序中某些对象或信息的创建、销毁、增加、修改、删除等动作,然后做出相应的响应处理。当对象的状态发生变化时,服务器自动调用监听器的方法。临听器常用于统计在线人数、在线用户、系统加载时的信息初始化等。
Servlet中的监听器分为以下3种类型:
1、监听ServletContext、Request、 Session 作用域的创建和销毁
- ServletContextListener:监听 ServeltContext。
- HttpSessionListener:监听新的 Session 创建事件。
- ServletRequestListener:监听 ServletRequest 的初始化和销毁。
2、监听ServletContext、Request、Session 作用域中属性的变化(增加、修改、删除)
- ServletContextAttributeListener:监听 Servlet 上下文参数的变化。
- HttpSessionAttributeListener:监听 HttpSession 参数的变化。
- ServletRequestAttributeListener:监听 ServletRequest 参数的变化。
3、监听HttpSession中对象状态的改变(被绑定、解除绑定、钝化、活化)
- HttpSessionBindingListener:监听 HttpSession ,并绑定及解除绑定。
- HtpSessonActivationListener:监听钝化和活动的 HttpSession 状态改变。
实现监听器
创建监听器类,监听 ServeltContext
通过注解 @WebListener 标注此类是监听类,代码如下:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class listenerDemo implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContex初始化");
System.out.println(servletContextEvent.getServletContext().getServerInfo());
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContex销毁");
System.out.println(servletContextEvent.getServletContext().getServerInfo());
}
}
开启监听器 Bean 扫描
在入口类上,添加注解 @ServletComponentScan
启动项目后,控制台输出:
如果不停止,在端口占用的情况下重新启动,则会显示:
创建监听器类,监听新的 Session 创建事件
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class listenerDemo implements HttpSessionListener{
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("MyListener sessionCreated-----");
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("MyListener sessionDestroyed-----");
}
}
创建控制器
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("test")
public class TestController {
@RequestMapping(value = "sessionTestSet", method = RequestMethod.GET)
public String sessionTestSet(HttpSession session) {
session.setAttribute("session", "测试监视器的 Session");
return "sessionTestSet";
}
@RequestMapping(value = "sessionTestGet", method = RequestMethod.GET)
public String sessionTestGet(HttpSession session) {
Object s = session.getAttribute("session");
session.removeAttribute("session");
return s.toString();
}
}
测试结果:
认识拦截器
拦截器的使用场景
处理所有请求共性问题:
- 乱码问题:用request,response参数去设置编码;
- 解决权限验证问题(是否登陆,取session对象查看);
拦截器与过滤器的区别
- 拦截器 Interceptor 依赖于框架容器,基于反射机制,只过滤请求;
- 过滤器 Filter 依赖于 Servlet 容器,基于回调函数,过滤范围大;
创建一个拦截器实现HandlerInterceptor接口
package com.jia.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("在controller执行之前调用");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("controller执行之后,且页面渲染之前调用");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面渲染之后调用,一般用于资源清理操作");
}
}
新建配置类来管理拦截器,并将之前的拦截器注入其中
package com.jia.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//扩展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// 拦截器配置
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index","/","/css/**","/js/**","img/**");
}
}
添加拦截:
addPathPatterns("/**")
排除拦截:
excludePathPatterns("/index","/","/css/**","/js/**","img/**")
控制类
@RequestMapping("interceptorTest")
public String interceptorTest() {
System.out.println("测试拦截器");
return "测试拦截器";
}