过滤器 Filter vs 拦截器 Interceptor

时间:2024-10-06 07:28:43

之前已经详细介绍过了过滤器Filter 和 拦截器Interceptor 了,本文将两种拦截请求的技术做一个对比讲解!

回顾:   过滤器Filter详解        拦截器Interceptor详解

一、过滤器 Filter

Filter,中文名过滤器,是 Servlet 规范中定义的一种拦截机制,如同应用的第一道防线——护城河,所有 HTTP 请求都要经过它的洗礼。它独立于 Spring 框架,地位超然,不受框架限制,任何 Web 应用都可以使用它来实现通用的请求处理逻辑。

1.1 Filter 的工作原理

想象一下,Web 应用就像一座城堡,而 Filter 就是城堡外围的护城河,所有进出城堡的请求都必须经过它。

  1. 请求抵达,Filter 拦截: 当浏览器发送请求到服务器时,请求首先会被 Servlet 容器拦截,就像任何访客都必须先经过护城河的检查。

  2. Filter 链,层层把关: 多个 Filter 可以组成一条链,按照预先定义的顺序依次对请求进行处理,就像护城河上设置了多道关卡,每道关卡都有不同的检查项目。

  3. FilterChain:放行或拦截的决定者: FilterChain 对象就像关卡的守卫队长,它负责调用 Filter 链中的下一个 Filter,或者在所有 Filter 执行完毕后将请求放行到目标资源,最终将响应返回给客户端。

  4. 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 就像城堡的城门守卫,在客人进入房间之前、离开房间之后,以及处理房间事务的过程中,都会提供周到的服务:

  1. preHandle: 第一道关卡

    • 在目标方法执行之前执行,就像城门守卫在客人进入房间之前进行身份验证。

    • 可以修改请求参数、设置响应头信息、甚至终止请求的执行。

    • 返回 true 表示放行,继续执行下一个拦截器或目标方法;返回 false 则表示拦截,请求终止,就像城门守卫拒绝客人进入。

  2. postHandle: 第二道关卡

    • 在目标方法执行之后,视图渲染之前执行,就像城门守卫在客人离开房间,但还没走出城门时进行检查。

    • 可以修改模型数据、添加逻辑视图等,对响应进行二次加工。

    • 此时无法阻止请求的处理流程,因为目标方法已经执行完毕。

  3. 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 的对比讲解了,希望对各位看官有所帮助,下期见,谢谢~