目录
一、什么是拦截器、拦截器的功能?
拦截器Interceptor同 Filter 过滤器一样,它俩都是面向切面编程——体现了AOP
的编程思想 ,拦截器可以根据 URL 对请求进行拦截,主要应用于登陆校验、日志记录、权限验证、性能监控等功能:
● 登录校验:判断用户是否登录。
● 日志记录:记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
● 权限检查:用户是否有权限访问资源,如校验token
● 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间,即监控请求处理时长等。
● 通用行为:读取 Cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。
二、拦截器与过滤器的区别
过滤器 和 拦截器均体现了AOP
的编程思想,都可以实现诸如日志记录、登录校验、权限验证等功能,但二者的不同点也是比较多的。
-
2.1、出身不同:
-
拦截器实现的是HandlerInterceptor接口,拦截器是属于Spring技术,它是
Spring的一个
组件,并由Spring
容器创建管理,并不依赖Tomcat服务器
,是可以单独使用的,拦截器不仅能应用在web
程序中,也可以用于Application
、Swing
等程序中;过滤器实现是javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat服务器
,Filter
是在Tomcat服务器创建的对象,导致它只能在web
程序中使用。 -
2.2、使用场景不同:
-
过滤器侧重于对请求参数进行过滤的, 比如敏感词过滤、字符集编码设置
CharacterEncodingFilter
、修改请求头和响应头的信息;拦截器是侧重于验证请求、截断请求,对用户请求做预先的判断处理、可以修改ModelAndView对象中的数据和视图,影响控制器方法最终的执行结果的,拦截器主要应用于登陆校验、日志记录、权限验证、性能监控等功能: -
2.3、触发时机不同:
-
过滤器
Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。拦截器
Interceptor
是在请求进入servlet
后,在进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。 -
过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对
Controller
中请求或访问static
目录下的资源请求起作用。 -
2.4、实现不同:
-
过滤器是基于方法回调实现的,当我们要执行下一个过滤器或下一个流程时,需要调用 FilterChain 对象的 doFilter 方法进行回调执行,如下图所示:
-
拦截器是基于动态代理(底层是反射)实现的。
三、拦截器在springboot中如何使用
拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor
,一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。
在 Spring Boot 项目中,使用拦截器功能通常需要以下 3 步:
- 定义拦截器;
- 注册拦截器;
- 指定拦截规则(如果是拦截所有,静态资源也会被拦截)。
3.1 定义拦截器:
创建一个普通类,并实现 HandlerInterceptor 接口,
然后实现HandlerInterceptor 接口中的 3 个方法:
返回值类型 | 方法声明 | 描述 |
---|---|---|
boolean | preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) (请求对象、响应对象、被拦截的控制器对象) | 该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。 |
void | postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) | 该方法在控制器方法执行完之后、解析视图之前执行的,可以通过此方法对请求域中的数据和视图做进一步修改,可以对执行结构进行二次修正。 |
void | afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) | 该方法在视图渲染结束后执行,也就是请求处理完成之后,可以通过此方法实现资源清理、记录日志信息等工作。 |
package net.biancheng.www.componet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行前
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser == null) {
//未登录,返回登陆页
request.setAttribute("msg", "您没有权限进行此操作,请先登陆!");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
} else {
//放行
return true;
}
}
/**
* 目标方法执行后
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}", modelAndView);
}
/**
* 页面渲染后
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}", ex);
}
}
3.2 注册拦截器:
创建一个实现了 WebMvcConfigurer 接口的配置类(使用了 @Configuration 注解的类),重写 addInterceptors() 方法,并在该方法中调用 registry.addInterceptor() 方法将自定义的拦截器注册到容器中。
在配置类 MyMvcConfig 中,添加以下方法注册拦截器,代码如下:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
......
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor());
}
}
3.3 指定拦截规则:
@Slf4j
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
......
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("注册拦截器");
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //拦截所有请求,包括静态资源文件
.excludePathPatterns("/", "/login", "/index.html", "/user/login", "/css/**", "/images/**", "/js/**", "/fonts/**"); //放行登录页,登陆操作,静态资源
}
}
在指定拦截器拦截规则时,调用了两个方法,这两个方法的说明如下:
- addPathPatterns:该方法用于指定拦截路径,例如拦截路径为“/**”,表示拦截所有请求,包括对静态资源的请求。
- excludePathPatterns:该方法用于排除拦截路径,即指定不需要被拦截器拦截的请求。
四、过滤器的使用举例:
字符集编码设置举例:CharacterEncodingFilter
类实现了Filter
接口,并重写了doFilter
方法。在这个方法中,我们将请求和响应的字符编码设置为UTF-8:
@Component
public class CharacterEncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
}