拦截器与Filter的区别
首先拦截器(Intercepter)和过滤器(Filter)都是Web项目中针对Request请求的处理组件,在请求到达业务处理逻辑前,进行预处理,包括监控、安全相关的职责。
所处位置
首先SpringBoot的拦截器本质是SpringMVC的一部分,SpringMVC的核心是DispatcherServlet
,DispatcherServlet
是一个特殊的 Servlet。它实现了 Servlet 接口,并遵循 Servlet 的生命周期。在 Spring MVC 中,DispatcherServlet
是最顶层的 Servlet,它扩展了 Servlet 的功能,提供了 Spring MVC 特有的请求处理机制。
也就是说SpringMVC是基于DispatcherServlet
工作,而DispatcherServlet
是基于J2EE的Servlet的一种二次抽象封装。
过滤器(Filter)工作在 Servlet 规范中,它们在请求进入 DispatcherServlet
之前被调用,也就是说,它们在 Spring MVC 的上下文之外工作。
拦截器(Intercepter)则是SpringMVC内部的概念,它工作在 Spring MVC 的 DispatcherServlet
中,这意味着它们在 Spring 的整个请求处理流程中有多个介入点。
总的来讲,在职能上,过滤器和拦截器都可以对请求和响应进行预处理,比如设置请求属性、修改请求和响应内容等。具体实现时由于过滤器在 Spring 的请求处理流程之外,因此它们无法访问 Spring 的上下文或 Bean,在Spring项目中不如拦截器方便。可以理解过滤器(Filter)是Web容器级的,而拦截器(Intercepter)是框架级的。
值处理
拦截器(Intercepter)可以介入到请求的处理流程中的以下阶段:
- preHandle:在控制器方法执行之前调用,可以决定是否继续执行后续的拦截器或控制器。如果返回
true
,请求继续向下传递;如果返回false
,请求将被中断,后续的拦截器和控制器将不会被调用。 - postHandle:在控制器方法执行之后,视图渲染之前调用。此时可以对控制器的返回值进行操作。
- afterCompletion:在请求处理完成之后,也就是渲染了视图后调用。这个阶段可以用来进行资源清理工作。
过滤器(Filter)主要介入以下阶段:
- doFilter:在请求到达
DispatcherServlet
之前调用。过滤器可以对请求和响应进行预处理,比如设置请求属性、修改请求和响应内容等。
拦截器(Intercepter)的 preHandle
方法具有返回值,这是一个布尔值,用于控制请求是否继续向下传递。这个返回值提供了一种机制,允许拦截器根据预处理的结果来决定是否继续执行后续的拦截器或控制器。例如,如果一个拦截器用于权限检查,并且当前用户没有权限执行某个操作,那么它可以返回 false
来阻止请求继续传递,并可能转发到错误页面或返回一个错误响应。
过滤器(Filter)的 doFilter
方法没有返回值。过滤器通过直接操作请求和响应对象来实现其功能。如果需要中断请求或改变请求的流向,过滤器可以通过修改请求对象或抛出异常来实现。例如,如果过滤器检测到非法请求,它可以修改请求对象,将其转发到一个错误处理页面,或者直接设置响应状态码和响应内容,然后结束请求。
总之,拦截器通过返回值提供了更灵活的控制机制,可以在请求处理的不同阶段决定是否继续处理请求。而过滤器则通过直接操作请求和响应对象来实现控制,它们在请求进入 Spring MVC 之前提供了一种预处理机制。
拦截器的使用
使用拦截器主要包含两步:
- 自定义拦截器类
- 注册拦截器到Spring中
开发拦截器
自定义 Interceptor 的话必须实现 org.springframework.web.servlet.HandlerInterceptor接口或继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter类。然后实现三个方法:
- preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)
- postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
- afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
preHandler方法
该方法在处理之前最先被调用,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。
postHandler方法
postHandler是当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。针对大部分前后端分离的项目,此方法使用较少。
afterCompletion方法
需要在当前对应的 Interceptor 类的 postHandler 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。一般不使用该方法。
我们编写用于请求认证的拦截器:
@Compont //对于Spring内部,建议使用注解,使其统一交给Spring进行bean管理
public class RefererIntercepter extends HandlerInterceptorAdapter {@Autowiredprivate SystemService system;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String referer = request.getHeader("Referer");if(system.checkReferer(referer)){return true;}return false;}//继承HandlerInterceptorAdapter 的方式,对于不使用的postHandle、afterCompletion可以不用实现
}
@Compont //对于Spring内部,建议使用注解,使其统一交给Spring进行bean管理
public class LoginCheckIntercepter extends HandlerInterceptorAdapter {@Autowiredprivate UserService userService;@Autowiredprivate CookieService cookieService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Cookie[] cookie = request.getCookies();String id = cookieService.getTokenId(cookie);String seesionId = request.getSessionId();if(!userService.timeoutSession(sessionId) && userService.isInvaild(id)){userService.refresh(seesionId);return true;}return false;}//继承HandlerInterceptorAdapter 的方式,对于不使用的postHandle、afterCompletion可以不用实现
}
注册拦截器
基于WebMvcConfigurer接口,实现SpringMVC的Web配置类,通过addInterceptors将拦截器注入:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowired private RefererIntercepter referer;@Autowired LoginCheckIntercepter loginCheck;private @Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册referer拦截,order设置为最先拦截,并指定url前缀为innerPage的接口registry.addInterceptor(referer).order(0).addPathPatterns("/innerPage/**");//注册登录认证拦截,排除url前缀为exit和test的接口registry.addInterceptor(loginCheck).order(1).addPathPatterns("/admin/oldLogin").excludePathPatterns("/exit/**","test/**");}
}
补充:WebMvcConfigurer方法
-
addInterceptors(InterceptorRegistry registry): 该方法用于注册拦截器(Interceptor),可以通过
registry.addInterceptor(拦截器Bean)
添加自定义拦截器,并使用addPathPatterns
指定拦截路径或excludePathPatterns
指定排除路径。
@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).order(1)//拦截顺序.addPathPatterns("/api/**").excludePathPatterns("/api/exit/**");//剔除的URL
}
-
addCorsMappings(CorsRegistry registry): 用于添加跨域请求配置。可以在
CorsRegistry
中配置哪些路径需要跨域支持,以及支持哪些域名、方法和头信息等。
@Override
public void addCorsMappings(CorsRegistry registry) {// 添加跨域请求配置,允许指定路径下的所有域名进行跨域请求registry.addMapping("/api/**").allowedOrigins("http://example.com").allowedMethods("GET", "POST").allowedHeaders("*").allowCredentials(true);
}
-
addResourceHandlers(ResourceHandlerRegistry registry): 通过该方法可以添加静态资源的处理规则。通常用于指定静态资源的路径和位置,如图片、JavaScript 文件和 CSS 文件等。
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {// 配置静态资源的访问路径和位置registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
-
configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer): 允许配置默认的 Servlet 处理,例如启用或禁用默认的 Servlet 处理。
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {// 启用默认的 Servlet 处理configurer.enable();
}
-
configureViewResolvers(ViewResolverRegistry registry): 用于配置视图解析器,它可以将控制器返回的视图名称解析为具体的视图实现。
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {// 配置视图解析器,指定 JSP 文件的位置和后缀registry.jsp("/WEB-INF/views/", ".jsp");
}
-
configureContentNegotiation(ContentNegotiationConfigurer configurer): 用于配置内容协商策略,即如何根据请求头中的信息决定响应的格式(如 JSON、XML 等)。
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {// 配置内容协商策略,决定响应的格式configurer.favorPathExtension(true).ignoreAcceptHeader(true).useRegisteredExtensionsOnly(false).defaultContentType(MediaType.APPLICATION_JSON);
}
-
addViewControllers(ViewControllerRegistry registry): 用于注册特定的视图控制器,可以实现 URL 到视图的直接映射,而不需要通过控制器方法。
@Override
public void addViewControllers(ViewControllerRegistry registry) {// 注册一个视图控制器,直接映射 URL 到视图页面registry.addViewController("/home").setViewName("home");
}
-
configureMessageConverters(List<HttpMessageConverter<?>> converters): 允许配置或添加自定义的消息转换器,这些转换器负责处理 HTTP 请求和响应的转换。
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// 添加自定义的消息转换器到转换器列表中converters.add(new MappingJackson2HttpMessageConverter());
}
-
extendMessageConverters(List<HttpMessageConverter<?>> converters): 用于在现有消息转换器的基础上添加额外的转换器,而不替换默认的转换器。
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {// 在现有消息转换器列表中添加额外的自定义转换器converters.add(new MyCustomMessageConverter());
}