之前已经详细介绍过了过滤器Filter 和 拦截器Interceptor 了,本文将两种拦截请求的技术做一个对比讲解!
回顾: 过滤器Filter详解 拦截器Interceptor详解
一、过滤器 Filter
Filter,中文名过滤器,是 Servlet 规范中定义的一种拦截机制,如同应用的第一道防线——护城河,所有 HTTP 请求都要经过它的洗礼。它独立于 Spring 框架,地位超然,不受框架限制,任何 Web 应用都可以使用它来实现通用的请求处理逻辑。
1.1 Filter 的工作原理
想象一下,Web 应用就像一座城堡,而 Filter 就是城堡外围的护城河,所有进出城堡的请求都必须经过它。
-
请求抵达,Filter 拦截: 当浏览器发送请求到服务器时,请求首先会被 Servlet 容器拦截,就像任何访客都必须先经过护城河的检查。
-
Filter 链,层层把关: 多个 Filter 可以组成一条链,按照预先定义的顺序依次对请求进行处理,就像护城河上设置了多道关卡,每道关卡都有不同的检查项目。
-
FilterChain:放行或拦截的决定者: FilterChain 对象就像关卡的守卫队长,它负责调用 Filter 链中的下一个 Filter,或者在所有 Filter 执行完毕后将请求放行到目标资源,最终将响应返回给客户端。
-
Filter 的生命周期:
-
init(FilterConfig): 在 Filter 初始化时调用一次,可以用来读取配置文件、初始化资源等,就像关卡建立时进行的准备工作。
-
doFilter(ServletRequest, ServletResponse, FilterChain): 每次请求都会调用,实现具体的过滤逻辑,就像每次检查访客都要执行的操作。
-
destroy(): 在 Filter 销毁时调用一次,可以用来释放资源,就像关卡拆除时进行的清理工作。
-
1.2 Spring Boot 中使用 Filter
Spring Boot 为我们提供 @WebFilter 注解,让我们可以用更简洁、优雅的方式配置 Filter:
1.2.1 创建 Filter
@WebFilter(urlPatterns = {"/user/*", "/admin/*"}, filterName = "securityFilter")
// 使用 @WebFilter 注解声明这是一个 Filter,并指定拦截路径和过滤器名称
public class SecurityFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化资源,例如加载用户权限配置文件
System.out.println("SecurityFilter 初始化...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 获取请求信息,例如请求路径、请求参数
HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();
// 2. 执行业务逻辑,例如校验用户登录状态、权限验证
System.out.println("SecurityFilter 拦截到请求: " + requestURI);
if (!isAuthenticated(httpRequest)) { // 模拟用户认证逻辑
// 未通过验证,跳转到登录页面或者返回错误信息
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendRedirect("/login"); // 跳转到登录页面
return;
}
// 3. 放行请求到下一个 Filter 或目标资源
chain.doFilter(request, response);
// 4. 后置处理,例如记录日志
System.out.println("SecurityFilter 处理请求完成: " + requestURI);
}
@Override
public void destroy() {
// 释放资源
System.out.println("SecurityFilter 销毁...");
}
// 模拟用户认证逻辑
private boolean isAuthenticated(HttpServletRequest request) {
// 这里可以根据实际需求实现,例如检查 Session 中是否存在用户信息
return true;
}
}
1.2.2 配置 Filter
在 Spring Boot 应用启动类上添加 @ServletComponentScan 注解,自动扫描并注册 Filter:
@SpringBootApplication
@ServletComponentScan // 启用 Servlet 组件扫描,自动发现并注册 Filter
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
1.3 Filter 的应用场景
-
安全防护: 就像城堡的护城河,Filter 是应用的第一道防线,可以用于实现统一的身份认证、权限校验、敏感信息过滤等安全控制。
-
字符编码: 可以统一设置请求和响应的字符编码,避免中文乱码问题,就像统一了城堡内使用的语言。
-
日志记录: 可以记录所有请求和响应的信息,方便排查问题、分析用户行为,就像城堡的史官记录着一切来往人员的信息。
-
gzip 压缩: 可以对响应数据进行 gzip 压缩,减少网络传输量,提高响应速度,就像优化了城堡的交通运输系统。
二、拦截器 Interceptor
Interceptor,中文名拦截器,是 Spring MVC 框架提供的一种更精细化的拦截机制,它专注于拦截控制器方法,就像城堡的城门守卫,只对进入城门的人进行检查。
2.1 Interceptor 的工作原理:三次拦截
想象一下,Interceptor 就像城堡的城门守卫,在客人进入房间之前、离开房间之后,以及处理房间事务的过程中,都会提供周到的服务:
-
preHandle: 第一道关卡
-
在目标方法执行之前执行,就像城门守卫在客人进入房间之前进行身份验证。
-
可以修改请求参数、设置响应头信息、甚至终止请求的执行。
-
返回 true 表示放行,继续执行下一个拦截器或目标方法;返回 false 则表示拦截,请求终止,就像城门守卫拒绝客人进入。
-
-
postHandle: 第二道关卡
-
在目标方法执行之后,视图渲染之前执行,就像城门守卫在客人离开房间,但还没走出城门时进行检查。
-
可以修改模型数据、添加逻辑视图等,对响应进行二次加工。
-
此时无法阻止请求的处理流程,因为目标方法已经执行完毕。
-
-
afterCompletion: 最后一道关卡
-
在请求处理完成后执行,包括视图渲染以及异常处理,就像城门守卫在客人离开城门后进行最后的记录工作。
-
可以进行资源清理、记录日志等操作,无论请求是否成功都会执行。
-
2.2 Spring Boot 中使用 Interceptor
2.2.1 创建 Interceptor
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 获取请求信息,例如请求路径、请求参数
String requestURI = request.getRequestURI();
// 2. 执行业务逻辑,例如权限验证
System.out.println("AuthInterceptor 拦截到请求: " + requestURI);
if (!hasPermission(request)) { // 模拟权限验证逻辑
// 如果验证失败,设置错误信息,并跳转到错误页面
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
request.setAttribute("message", "您没有权限访问该资源!");
request.getRequestDispatcher("/error").forward(request, response);
return false;
}
// 3. 放行请求
System.out.println("AuthInterceptor 放行请求: " + requestURI);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 修改模型数据,例如添加用户信息
if (modelAndView != null) {
modelAndView.addObject("username", "John Doe");
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 记录日志信息
System.out.println("AuthInterceptor 请求处理完成: " + request.getRequestURI());
if (ex != null) {
System.out.println("发生异常: " + ex.getMessage());
}
}
// 模拟权限验证逻辑
private boolean hasPermission(HttpServletRequest request) {
// 这里可以根据实际需求实现,例如检查用户角色
return true;
}
}
2.2.2 配置 Interceptor
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器,并配置拦截路径
registry.addInterceptor(new AuthInterceptor())
.addPathPatterns("/admin/**") // 拦截 /admin/ 下的所有请求
.excludePathPatterns("/admin/login"); // 排除 /admin/login 请求
}
}
2.3 Interceptor 的应用场景
-
权限控制: 就像城堡的城门守卫,Interceptor 可以拦截特定 URL 的请求,验证用户权限,防止未授权访问。
-
性能监控: 可以记录请求处理时间,分析系统性能瓶颈,就像城堡的计时器,记录每个访客的停留时间。
-
日志记录: 可以记录用户的关键操作日志,例如登录、退出、修改信息等,就像城堡的记录员,记录着重要事件。
-
国际化: 可以根据用户浏览器语言设置,选择合适的语言资源文件,就像城堡提供多语言导览服务。
三、Filter vs Interceptor
特性 | Filter | Interceptor |
规范 | Servlet 规范,不依赖 Spring 框架 | Spring MVC 框架的一部分 |
拦截范围 | 所有 Web 应用,拦截所有请求 | 仅限 Spring MVC 应用,拦截控制器方法 |
执行顺序 | Filter 链式调用,按配置顺序执行 | Interceptor 链式调用,按配置顺序执行 |
方法丰富度 | 只有一个 doFilter 方法 | preHandle、postHandle、afterCompletion 三个方法 |
应用场景 | 偏向底层、通用性需求,例如字符编码、安全过滤、Gzip 压缩 | 偏向业务逻辑,例如权限控制、日志记录、性能监控 |
选择建议:
-
优先考虑 Interceptor: 如果你的应用是基于 Spring MVC 构建的,并且需要对控制器方法进行拦截,那么优先选择 Interceptor,它更方便、更灵活。
-
Filter 也不可或缺: 如果你的需求与 Servlet API 相关,例如文件上传下载、字符编码设置等,或者你需要在 Spring MVC 应用之外使用拦截器,那么 Filter 是更好的选择。
-
强强联手,灵活组合: 在实际项目中,可以根据需要组合使用 Filter 和 Interceptor,例如使用 Filter 进行字符编码设置,使用 Interceptor 进行权限控制,从而构建更完善的请求处理机制。
总结:
Filter 和 Interceptor 都是 Spring Boot 应用中不可或缺的请求拦截利器,它们就像城堡的护城河和城门守卫,在不同阶段、不同层面守护着应用的安全。开发者需要根据具体的应用场景选择合适的拦截器,并充分利用 Spring Boot 提供的便捷 API 和灵活的配置,才能构建出安全、高效、用户体验俱佳的 Web 应用。
以上就是关于过滤器Filter 和 拦截器Interceptor 的对比讲解了,希望对各位看官有所帮助,下期见,谢谢~