文章目录
前言
在项目开发中,我们经常会遇到需要拦截器(Interceptor)或者过滤器(Filter)的需求,但是我们什么时候该用过滤器,什么时候该用拦截器呢?
一、区别
我们来一一介绍一下
1、Filter
Filter是由Servlet提供的组件,实现接口javax.servlet.Filter,其内部基于回调函数实现,通过调用FilterChain类下的doFilter方法进行不断回调。
在客户端请求进入Servlet之前进行调用处理。
其会对所有请求进行增强处理。
@WebFilter("/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String url = request.getRequestURI();
if (url.contains("/admin.jsp")||url.contains("/register.jsp")){
filterChain.doFilter(request,response);
return;
}
if (url.contains("/js")||url.contains("/css")||url.contains("/fonts")||url.contains("/images")||url.contains("/lib")||url.contains("/layui")){
filterChain.doFilter(request,response);
return;
}
String user = (String) request.getSession().getAttribute("User");
if(user!=null){
filterChain.doFilter(request,response);
return;
}
if (url.contains("/home")||url.contains("/register")) {
filterChain.doFilter(request,response);
return;
}
response.sendRedirect("admin.jsp");
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
我们来分析一下原理:
- 当服务器初始化时,Filter中的init()方法会被执行调用,创建Filter。
- 当客户端请求进tomcat服务器之后,Filter处理对应的请求进入doFilter方法
- 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作。
2.Interceptor
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。其是基于反射实现的。Interceptor仅针对SpringMVC的访问进行拦截增强。
看一下我这边在springboot中如何使用拦截器的:
@Component
@Slf4j
@RequiredArgsConstructor
public class TokenInterceptor implements HandlerInterceptor {
private final IUserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("X-Token");
if (token!=null){
Long userId = JwtHelper.getUserId(token);
String username = JwtHelper.getUsername(token);
User user = userService.getOne(new LambdaQueryWrapper<User>()
.eq(User::getId, userId)
.eq(User::getUsername, username)
);
if (ObjectUtil.isNotNull(user)) {
log.debug("验证通过");
return true;
}
}
log.debug("验证失败");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSON.toJSONString(Result.fail(20003,"无效,请重新登录")));
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
首先定然是要先实现HandlerInterceptor这个接口,别忘了把他交给spring管理。
同时还有配置类:
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(
"/user/login",
"/user/info",
"/user/logout",
"/doc.html/**",
"/webjars/**",
"/swagger-resources/**"
);
}
}
我们来看看拦截器里面的方法:
前置处理方法
控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法。
- 返回false表示拦截,即不调用控制器方法。
- 返回true表示不拦截,放行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle()方法被执行!");
return true;
}
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装
使用handler参数,可以获取方法的相关信息
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod hm = (HandlerMethod)handler;
String methodName = hm.getMethod().getName();//可以获取方法的名称
System.out.println("preHandle..."+methodName);
return true;
}
后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
@Override
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView mv) throws Exception {
System.out.println("postHandle()方法被执行!");
}
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
因为咱们现在都是返回json数据,所以该参数的使用率不高。
完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion()方法被执行!");
}
注:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理 ,因为我们现在已经有全局异常处理器类,所以该参数的使用率也不高。
这三个方法中,最常用的是preHandle(),在这个方法中可以通过返回值来决定是否要进行放行,我们可以把业务逻辑放在该方法中,如果满足业务则返回true放行,不满足则返回false拦截。
过滤器和拦截器如何指定加载顺序
(1)Filter:
需要通过配置类指定加载顺序,值越小,越先执行。采用@WebFilter无法指定顺序。
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/user/*"));
filterRegistrationBean.setOrder(2);
return filterRegistrationBean;
}
(2)Interceptor:
需要通过配置类指定加载顺序,值越小,越先执行。
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// getMyInterceptor()这种注册方式可以在拦截器里注入bean
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(1);
// 这种注册方式由于是自己new出来的,所以在拦截器里注册的bean都为null
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
}
}
二、应用领域
Filter
应用领域比较少。在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等。
Interceptor
处理各种复杂请求,比Filter功能更加强大