首页 > 其他分享 >过滤器与拦截器

过滤器与拦截器

时间:2024-08-19 15:27:05浏览次数:6  
标签:拦截器 请求 Spring Filter doFilter 过滤器 Interceptor public

过滤器 与拦截器

参考

https://www.cnblogs.com/Black-Ice/p/16248535.html

过滤器 Filter

Filter 基本介绍

过滤器 Filter 是 Sun 公司在 Servlet 2.3 规范中添加的新功能,其作用是对客户端发送给 Servlet 的请求以及对 Servlet 返回给客户端的响应做一些定制化的处理,例如校验请求的参数、设置请求/响应的 Header、修改请求/响应的内容等。

Filter 引入了过滤链(Filter Chain)的概念,一个 Web 应用可以部署多个 Filter,这些 Filter 会组成一种链式结构,客户端的请求在到达 Servlet 之前会一直在这个链上传递,不同的 Filter 负责对请求/响应做不同的处理。 Filter 的处理流程如下图所示:

image

AOP 编程思想

在深入理解 Filter 之前,我们先聊一聊面向切面编程(Aspect Oriented Programming,AOP)。AOP 不是一种具体的技术,而是一种编程思想,它允许我们在不修改源码的基础上实现方法逻辑的增强,也就是在方法执行前后添加一些自定义的处理。

Filter 是 AOP 编程思想的一种体现,其作用可认为是对 Servlet 功能的增强。Filter 可以对用户的请求做预处理,也可以对返回的响应做后处理,且这些处理逻辑与 Servlet 的处理逻辑是分隔开的,这使得程序中各部分业务逻辑之间的耦合度降低,从而提高了程序的可维护性和可扩展性。

创建 Filter

创建 Filter 需要实现 javax.servlet.Filter 接口,或者继承实现了 Filter 接口的父类。Filter 接口中定义了三个方法:

  • init:在 Web 程序启动时被调用,用于初始化 Filter。
  • doFilter:在客户端的请求到达时被调用,doFilter 方法中定义了 Filter 的主要处理逻辑,同时该方法还负责将请求传递给下一个 Filter 或 Servlet。
  • destroy:在 Web 程序关闭时被调用,用于销毁一些资源。

下面我们通过实现 Filter 接口来创建一个自定义的 Filter:

public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println(filterConfig.getFilterName() + " 被初始化");
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        System.out.println("Filter 拦截到了请求: " + request.getRequestURL());
        System.out.println("Filter 对请求做预处理...");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("Filter 修改响应的内容...");
    }
 
    @Override
    public void destroy() {
        System.out.println("Filter 被回收");
    }
}
  • init 方法的 filterConfig 参数封装了当前 Filter 的配置信息,在 Filter 初始化时,我们将 Filter 的名称打印在控制台。
  • doFilter 方法定义了 Filter 拦截到用户请求后的处理逻辑,filterChain.doFilter(servletRequest, servletResponse); 指的是将请求传递给一下个 Filter 或 Servlet,如果不添加该语句,那么请求就不会向后传递,自然也不会被处理。在该语句之后,可以添加对响应的处理逻辑(如果要修改响应的 Header,可直接在该语句之前修改;如果要修改响应的内容,则需要在该语句之后,且需要自定义一个 response)。
  • destroy 方法中,我们输出 "Filter 被回收" 的提示信息。

配置 Filter

Spring 项目中,我们可以使用 @Configuration + @Bean + FilterRegistrationBean 对 Filter 进行配置:

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<TestFilter> registryFilter() {
        FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TestFilter());
        registration.addUrlPatterns("/*");
        registration.setName("TestFilter");
        registration.setOrder(0);
        return registration;
    }
}

上述代码中,setFilter 方法用于设置 Filter 的类型;addUrlPatterns 方法用于设置拦截的规则;setName 方法用于设置 Filter 的名称;setOrder 方法用于设置 Filter 的优先级,数字越小优先级越高。

测试 Filter

接下来,我们定义一个简单的 Web 服务,测试 Filter 是否生效:

@RestController
public class UserController {
 
    @RequestMapping(path = "/hello", method = RequestMethod.GET)
    public String sayHello() {
        System.out.println("正在处理请求...");
        System.out.println("请求处理完成~");
        return "I'm fine, thank you.";
    }
}

创建 Filter 的其它方式

1. @WebFilter 注解 + 包扫描

除了 FilterRegistrationBean 外,Servlet 3.0 引入的注解 @WebFilter 也可用于配置 Filter。我们只需要在自定义的 Filter 类上添加该注解,就可以设置 Filter 的名称和拦截规则:

实现 Filter 接口

添加 @WebFilter 注解 设置拦截规则

在spring配置类添加 @ServletComponetSca并指定扫描的包

@WebFilter(urlPatterns = "/*", filterName = "TestFilter")
public class TestFilter implements Filter {
    // 省略部分代码
}

由于@WebFilter 并非 Spring 提供,因此若要使自定义的 Filter 生效,还需在配置类上添加 @ServletComponetScan 注解,并指定扫描的包:

@SpringBootApplication
@ServletComponentScan("com.example.filter")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

需要注意的是,@WebFilter 注解并不允许我们设置 Filter 的执行顺序,且在 Filter 类上添加 @Order 注解也是无效的。如果项目中有多个被 @WebFilter 修饰的 Filter,那么这些 Filter 的执行顺序由其 "类名的字典序" 决定,例如类名为 "Axx" 的 Filter 的执行顺序要先于类名为 "Bxx" 的 Filter。

添加了 @WebFilter 注解后就不要再添加 @Component 注解了,如果都添加,那么系统会创建两个 Filter。

2. @Component 注解

Spring 项目中,我们可以通过添加 @Component 注解将自定义的 Bean 交给 Spring 容器管理。同样的,对于自定义的 Filter,我们也可以直接添加 @Component 注解使其生效,而且还可以添加 @Order 注解来设置不同 Filter 的执行顺序。

实现 Filter 接口

添加注解@Component 将其注册到 Spring 容器

@Component
@Order(1)
public class TestFilter implements Filter {
    // 省略部分代码
}

此种配置方式一般不常使用,因为其无法设置 Filter 的拦截规则,默认的拦截路径为 /*

虽然不能配置拦截规则,但我们可以在 doFilter 方法中定义请求的放行规则,例如当请求的 URL 匹配我们设置的规则时,直接将该请求放行,也就是立即执行 filterChain.doFilter(servletRequest, servletResponse);

**3. 继承 OncePerRequestFilter (推荐使用) **

OncePerRequestFilter 是一个由 Spring 提供的抽象类,在项目中,我们可以采用继承 OncePerRequestFilter 的方式创建 Filter,然后重写 doFilterInternal 方法定义 Filter 的处理逻辑,重写 shouldNotFilter 方法设置 Filter 的放行规则。对于多个 Filter 的执行顺序,我们也可以通过添加 @Order 注解进行设置。当然,若要使 Filter 生效,还需添加 @Component 注解将其注册到 Spring 容器。

继承OncePerRequestFilter

添加注解@Component 将其注册到 Spring 容器

@Component
@Order(1)
public class CSpringFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 处理逻辑
    }
 
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        // 放行规则
    }
}

实际上,方式 2 和方式 3 本质上并没有什么区别,因为 OncePerRequestFilter 底层也是通过实现 Filter 接口来达到过滤请求/响应的目的,只不过 Spring 在 OncePerRequestFilter 中帮我们封装了许多功能,因此更推荐采用此种方式创建 Filter。

Filter 的优先级

上文中提到,使用配置类或添加 @Order 注解可以显式的设置 Filter 的执行顺序,修改类名可以隐式的设置 Filter 的执行顺序。如果项目中存在多个 Filter,且这些 Filter 由不同的方式创建,那么它们的执行顺序是怎样的呢?

能够确定的是,Spring 根据 Filter 的 order 决定其优先级,如果我们通过配置类或者通过 @Order 注解设置了 Filter 的 order,那么 order 值越小的 Filter 的优先级越高,无论 Filter 由何种方式创建。如果多个 Filter 的优先级相同,那么执行顺序为:

  1. 配置类中配置的 Filter 优先执行,如果配置类中存在多个 Filter,那么 Spring 按照其在配置类中配置的顺序依次执行。
  2. @WebFilter 注解修饰的 Filter 之后执行,如果存在多个 Filter,那么 Spring 按照其类名的字典序依次执行。
  3. @Component 注解修饰的 Filter 最后执行,如果存在多个 Filter,那么 Spring 按照其类名的字典序依次执行。

注意,以上优先级顺序仅适用于 order 相同的特殊情况。如果我们不配置 Filter 的 order,那么 Spring 默认将其 order 设置为 LOWEST_PRECEDENCE = Integer.MAX_VALUE,也就是最低优先级。由于被 @WebFilter 注解修饰的 Filter 无法显式配置优先级,因此其 order 为 Integer.MAX_VALUE。本文所说的 Filter 的优先级指的是 Filter 对请求做预处理的优先级,对响应做后处理的优先级与之相反。

Filter 的应用场景

Filter 的常见应用场景包括:

  • 解决跨域访问:前后端分离的项目往往存在跨域访问的问题,Filter 允许我们在 response 的 Header 中设置 "Access-Control-Allow-Origin"、"Access-Control-Allow-Methods" 等头域,以此解决跨域失败问题。
  • 设置字符编码:字符编码 Filter 可以在 request 提交到 Servlet 之前或者在 response 返回给客户端之前为请求/响应设置特定的编码格式,以解决请求/响应内容乱码的问题。
  • 记录日志:日志记录 Filter 可以在拦截到请求后,记录请求的 IP、访问的 URL,拦截到响应后记录请求的处理时间。当不需要记录日志时,也可以直接将 Filter 的配置注释掉。
  • 校验权限:Web 服务中,客户端在发送请求时会携带 cookie 或者 token 进行身份认证,权限校验 Filter 可以在 request 提交到 Servlet 之前对 cookie 或 token 进行校验,如果用户未登录或者权限不够,那么 Filter 可以对请求做重定向或返回错误信息。
  • 替换内容:内容替换 Filter 可以对网站的内容进行控制,防止输入/输出非法内容和敏感信息。例如在请求到达 Servlet 之前对请求的内容进行转义,防止 XSS 攻击;在 Servlet 将内容输出到 response 时,使用 response 将内容缓存起来,然后在 Filter 中进行替换,最后再输出到客户浏览器(由于默认的 response 并不能严格的缓存输出内容,因此需要自定义一个具备缓存功能的 response)。

Filter 应用场景的相关内容参考自《Java Web 整合开发之王者归来》,好中二的书名

标签:拦截器,请求,Spring,Filter,doFilter,过滤器,Interceptor,public
From: https://www.cnblogs.com/pyb999/p/18367332

相关文章

  • 过滤器模式
    过滤器模式(FilterPattern)或标准模式(CriteriaPattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。双龙物流 概要意图用于将对象的筛选过程......
  • 科普文:MyBatis系列之【Mybatis常用的4种拦截器】
    MyBatis拦截器的工作原理MyBatis的拦截器是一个十分强大的特性,它可以让我们在MyBatis调用数据库操作的过程中插入自己的逻辑,非常适合做一些数据操作的审计、性能优化、事务管理、执行日志输出等。MyBatis的拦截器机制基于AOP(面向切面编程),允许在执行SQL语句前后插入自......
  • 设计模式 - 过滤器模式
    ......
  • Spring Cloud Gateway 实现简单自定义过滤器
    背景SpringCloudGateway是SpringCloud退出的第二代网关框架,我们可以用它来实现反向代理,路由转发,权限校验等功能,这里介绍一个它的基础功能,通过Filter机制实现一个简单的HTTP接口处理。从总体上来看SpringCloudGateway提供的过滤器可以分为两类,一种是对全局流量都......
  • gateway通过过滤器修改uri
    背景:有时要根据不同的请求头或者请求参数动态修改路由的uri实现方案:使用全局过滤器(很关键)https://blog.csdn.net/netyeaxi/article/details/115287385packagecom.tl.gateway.filter;importjava.net.URI;importjava.net.URISyntaxException;importorg.springframewo......
  • 过滤器、拦截器、AOP、ControllerAdvcie执行顺序对比
    过滤器、拦截器、AOP、ControllerAdvcie执行顺序对比0.执行顺序过滤器➡拦截器➡AOP➡ControllerAdvice➡Controller没有异常的情况下,执行顺序如下:有异常的情况下,执行顺序如下:tip:当产生异常后,无论是否有ControllerAdvice处理,HandlerInterceptor都不会执行post......
  • 任意大小的连续过滤器
    我有数据框IDfilter1filter20FalseFalse1FalseTrue2......
  • 海量 url 去重类问题(布隆过滤器)
    布隆过滤器(BloomFilter)是一种空间效率非常高的概率数据结构,主要用于判断一个元素是否在一个集合中。它由一个位数组和一组哈希函数组成,可以用于处理海量数据的去重问题,例如海量URL去重。布隆过滤器的基本原理位数组(BitArray):布隆过滤器使用一个位数组来存储数据。初......
  • 登录相关功能的优化【JWT令牌+拦截器+跨域】
    登录相关功能的优化登录后显示当前登录用户el-dropdown: Element-Theworld'smostpopularVueUIframework<el-dropdownstyle="float:right;height:60px;line-height:60px"><spanclass="el-dropdown-link"style="color:white;font-siz......
  • JavaWeb中Servlet过滤器的应用即防跳墙的添加
    原理:在Servlet和mybatis的基础环境上使用过滤器(对上篇学生管理系统的进一步完善)也是使用session进行判断和控制页面的跳转一、过滤器的基本概念: Servlet过滤器:  从字面上的字意理解为经过一层次的过滤处理才达到使用的要求,而其实Servlet过滤器就是服务器与客户端请求......