概述
过滤器(Filter)和拦截器(Interceptor)都是基于 AOP(Aspect Oriented Programming,面向切面编程)思想实现的.
过滤器和拦截器都是基于 AOP 思想实现的,用来处理某个统一的功能的,但二者又有 5 点不同:出身不同、触发时机不同、实现不同、支持的项目类型不同以及使用的场景不同。过滤器通常是用来进行全局过滤的,而拦截器是用来实现某项业务拦截的。
过滤器和拦截器的区别主要体现在以下 5 点:
- 触发时机不同;
- 出身不同;
- 实现不同;
- 支持的项目类型不同;
- 使用的场景不同
其他区别
1、过滤器基于函数回调、拦截器基于反射;
2、过滤器几乎对所有请求起作用,拦截器只对目标执行方法起作用;
3、过滤器对请求进行预处理、再交给Servlet处理并且生成响应,最后Filter再对服务器响应进行后处理;
4.拦截器不依赖与servlet容是依赖于spring容器,过滤器依赖与servlet容器。
在 action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一 次
拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用,可以限制用户对图片,文件以及其他资源的访问。
拦截器可以访问action请求上下文、值栈里的对象,而过滤器不能访问。
拦截器只能对 action 请求起作用,而过滤器则可以对几乎所有的请求起作用
拦截器可以在方法执行前调用(preHandle),方法执行后调用(postHandle),视图页面渲染后调用(afterCompletion)
触发时机不同
请求的执行顺序是:请求进入容器 > 进入过滤器 > 进入 Servlet > 进入拦截器 > 执行控制器(Controller),如下图所示:
所以过滤器和拦截器的执行时机也是不同的,过滤器会先执行,然后才会执行拦截器,最后才会进入真正的要调用的方法。

出身不同
过滤器来自于 Servlet,而拦截器来自于 Spring 框架,从上面代码中我们也可以看出,过滤器在实现时导入的是 Servlet 相关的包,如下图所示:


实现不同
过滤器是基于方法回调实现的,我们在上面实现过滤器的时候就会发现,当我们要执行下一个过滤器或下一个流程时,需要调用 FilterChain 对象的 doFilter 方法进行回调执行,如下图所示:

由此可以看出,过滤器的实现是基于方法回调的。
而拦截器是基于动态代理(底层是反射)实现的,它的实现如下图所示:

支持的项目类型不同
过滤器是 Servlet 规范中定义的,所以过滤器要依赖 Servlet 容器,它只能用在 Web 项目中;而拦截器是 Spring 中的一个组件,因此拦截器既可以用在 Web 项目中,同时还可以用在 Application 或 Swing 程序中。
使用的场景不同
因为拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:登录判断、权限判断、日志记录等业务。 而过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、字符集编码设置、响应数据压缩等功能。
过滤器 (Filter)
过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。
init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。
doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
2、应用场景
1)过滤敏感词汇(防止sql注入)
2)设置字符编码
3)URL级别的权限访问控制
4)压缩响应信息
实现方式使用spring boot提供的FilterRegistrationBean注册Filter
定义Filter:
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 {
log.info("MyFilter");
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
注册Filter
@Slf4j
@Order(1)
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 {
log.info("-----------------------MyFilter");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
Filter.super.destroy();
}
使用原生servlet注解定义Filter
@WebFilter(filterName = "LoginFilter" ,urlPatterns = "/*")
@Slf4j
@Order(2)
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("进入过滤器init");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("进入过滤器"+servletRequest.getRemoteAddr()+"|"+servletRequest.getRemoteHost()+"|"+servletRequest.getLocalPort()+"|"+servletRequest.getServerPort()
);
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
log.info("进入过滤器destroy");
Filter.super.destroy();
}
这里直接用@WebFilter就可以进行配置,同样,可以设置url匹配模式,过滤器名称等。这里需要注意一点的是@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在启动类中加另外一个注解:@ServletComponetScan,指定扫描的包。
拦截器(Interceptor)
实现方式
自定义拦截器
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
log.info("preHandle");
String clientIP = ServletUtil.getClientIP(httpServletRequest);
log.info("访问IP:"+clientIP);
log.info("请求路径:{}", httpServletRequest.getRequestURI());
return true;
}
@Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
log.info("postHandle");
}
@Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
log.info("afterCompletion");
}
注册拦截器
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
private final AuthInterceptor authInterceptor;
public WebMvcConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**");
}
}
应用场景
1.登录验证,判断用户是否登录。
2.权限验证,判断用户是否有权限访问资源,如校验token
3.日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
4.处理cookie、本地化、国际化、主题等。
5.性能监控,监控请求处理时长等。
6.通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)
WebMvcConfigurer
拦截器Interceptor
HandlerInterceptor
package com.nobody.config;
import com.nobody.context.UserContextResolver;
import com.nobody.interceptor.UserPermissionInterceptor;
import com.nobody.interceptor.UserPermissionInterceptorAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
private UserPermissionInterceptor userPermissionInterceptor;
private UserPermissionInterceptorAdapter userPermissionInterceptorAdapter;
private UserContextResolver userContextResolver;
@Autowired
public void setUserPermissionInterceptor(UserPermissionInterceptor userPermissionInterceptor) {
this.userPermissionInterceptor = userPermissionInterceptor;
}
@Autowired
public void setUserPermissionInterceptorAdapter(
UserPermissionInterceptorAdapter userPermissionInterceptorAdapter) {
this.userPermissionInterceptorAdapter = userPermissionInterceptorAdapter;
}
@Autowired
public void setUserContextResolver(UserContextResolver userContextResolver) {
this.userContextResolver = userContextResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可以添加多个拦截器,一般只添加一个
// addPathPatterns("/**") 表示对所有请求都拦截
// .excludePathPatterns("/base/index") 表示排除对/base/index请求的拦截
// 多个拦截器可以设置order顺序,值越小,preHandle越先执行,postHandle和afterCompletion越后执行
// order默认的值是0,如果只添加一个拦截器,可以不显示设置order的值
registry.addInterceptor(userPermissionInterceptor).addPathPatterns("/**")
.excludePathPatterns("/base/index").order(0);
// registry.addInterceptor(userPermissionInterceptorAdapter).addPathPatterns("/**")
// .excludePathPatterns("/base/index").order(1);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userContextResolver);
}
}
HandlerInterceptor
什么是拦截器:在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略
为什么需要拦截器:在做身份认证或者是进行日志的记录时,我们需要通过拦截器达到我们的目的。最常用的登录拦截、或是权限校验、或是防重复提交、或是根据业务像12306去校验购票时间,总之可以去做很多的事情
如何用拦截器:在spring中用拦截器需要实现HandlerInterceptor接口或者它的实现子类:HandlerInterceptorAdapter,同时在applicationContext.xml文件中配置拦截器
二、定义实现类
定义一个Interceptor 非常简单方式有:
1、类要实现Spring 的HandlerInterceptor 接口
2、类继承实现了HandlerInterceptor 接口的类,例如 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter
三、HandlerInterceptor方法
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
● preHandle:在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
● postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView (这个博主就基本不怎么用了);
● afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
拦截器实现
新建TestFilter
package com.xxx.core.filter;
import com.xxx.common.exception.FastRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestFilter extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(TestFilter.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
logger.info("request请求地址path[{}] uri[{}]", request.getServletPath(),request.getRequestURI());
//request.getHeader(String) 从请求头中获取数据
//从请求头中获取用户token(登陆凭证根据业务而定)
Long userId= getUserId(request.getHeader("H-User-Token"));
if (userId != null && checkAuth(userId,request.getRequestURI())){
return true;
}
//这里的异常是我自定义的异常,系统抛出异常后框架捕获异常然后转为统一的格式返回给前端, 其实这里也可以返回false
throw new FastRuntimeException(20001,"No access");
}
/**
* 根据token获取用户ID
* @param userToken
* @return
*/
private Long getUserId(String userToken){
Long userId = null;
return userId;
}
/**
* 校验用户访问权限
* @param userId
* @param requestURI
* @return
*/
private boolean checkAuth(Long userId,String requestURI){
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {}
}
新建WebAppConfigurer 实现WebMvcConfigurer接口
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可添加多个
registry.addInterceptor(new TestFilter()).addPathPatterns("/**");
}
....
}
辅助实现
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author lc
* @Description
* @Date 创建于 2021/7/21 下午12:44
*/
@Slf4j
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Autowired
private JWTProperties jwtProperties;
@Resource
private AiAdminService aiAdminService;
@Override
public void addCorsMappings(CorsRegistry registry) {
//允许所有形式的跨域请求
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.maxAge(3600);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
List<String> list = new ArrayList<>();
list.add("*");
corsConfiguration.setAllowedOrigins(list);
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
return new StringHttpMessageConverter(StandardCharsets.UTF_8);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(responseBodyConverter());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.debug("addInterceptors token认证 run ...");
AuthInterceptor authInterceptor = new AuthInterceptor(this.aiAdminService);
registry.addInterceptor(authInterceptor).excludePathPatterns(jwtProperties.getIgnorePathList());
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 2021/4/26
* 类说明:拦截器
* @author lc
*/
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
private AiAdminService aiAdminService;
/**
* 结构
* @param aiAdminService
*/
public AuthInterceptor(AiAdminService aiAdminService) {
this.aiAdminService = aiAdminService;
}
/**
* 在请求处理之前进行调用(Controller方法调用之前)
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String requestURI = request.getServletPath();
String token = request.getHeader(Constants.JWT_TOKEN);
log.debug("***AuthFilter拦截器拦截URI=[{}],验证token=[{}]", requestURI, token);
if (!StringUtils.hasText(token)) {
log.error(Constants.JWT_TOKEN + "为空,用户未登录");
throw new AiHelperServiceException(ExceptionEnum.UN_AUTHORIZED);
}
UserInfo userInfo = aiAdminService.getUserInfo(token, requestURI);
if(ObjectUtils.isEmpty(userInfo)){
throw new AiHelperServiceException(ExceptionEnum.UN_AUTHORIZED);
}else{
request.setAttribute(Constants.USER_SESSION, userInfo);
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
}
}
本文详细比较了过滤器和拦截器在触发时机、出身、实现方式、支持的项目类型和应用场景上的差异,强调了它们在Web开发中的各自角色:过滤器用于通用功能,拦截器更适合业务逻辑控制。
1244

被折叠的 条评论
为什么被折叠?



