拦截器与过滤器详解


一、过滤器

1. 定义方式

过滤器是 Servlet 规范中的一种组件,它可以在请求被处理之前或之后对请求和响应进行预处理和后处理,可以用于实现一些通用的功能,例如:请求编码转换、登录验证、权限控制、性能监控等。

过滤器可以通过注解方式或者 bean 实例注入的方式创建,但不管哪种方式都需要自定义创建过滤器实现类。

新建自定义过滤器类并实现 Filter 类,过滤器的核心业务逻辑即在 doFilter() 方法中。

public class MyFilter1 implements Filter {

    private final Logger LOGGER = LoggerFactory.getLogger(MyFilter1.class);

    @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 {
        LOGGER.info("filter-1 doFilter.");
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        // 打印请求参数信息
        Map<String, String> parameterMap = new HashMap<>();
        req.getParameterMap().keySet().forEach(it -> {
            parameterMap.put(it, req.getParameter(it));
        });
        LOGGER.info("filter-1: url=【{}】, params=【{}】", req.getRequestURI(), parameterMap);
        // 放行请求
        filterChain.doFilter(req, servletResponse);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

2. 注入方式

(1) 注解实现

通过注解实现即在自定义过滤器类上添加 @WebFilter 并在启动类上添加 @ServletComponentScan 即可。

若同时创建了多个过滤器,可通过 @Order 注解设置优先值,值越高优先级则越高。

@Order(1)
@WebFilter
public class MyFilter1 implements Filter {

    // 略去实现逻辑
}

@ServletComponentScan
@SpringBootApplication
public class DemoFilterApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoFilterApplication.class, args);
    }
}
(2) 配置实现

除了通过注解实现另一种方式就是通过注入 bean 对象实现,我更推荐这种方法,配置调整更为方便。

@Configuration
public class FilterConfig {

    /**
     * 可以获取原始的 http 请求与相应, 但无法获取请求要访问的类与方法以及参数
     */
    @Bean
    public FilterRegistrationBean<MyFilter1> orderFilter1() {
        FilterRegistrationBean<MyFilter1> filter = new FilterRegistrationBean<>();
        // 设置名称
        filter.setName("filter-1");
        // 设置作用路径
        filter.setUrlPatterns(Collections.singleton("/api/filter/demo1/*"));
        // 指定优先级
        filter.setOrder(-1);
        // 指定自定义过滤器
        filter.setFilter(new MyFilter1());
        return filter;
    }
}

3. 资源忽略

在上面的示例中通过 setUrlPatterns() 配置了拦截作用域,但拦截器并没有提供提供显式的白名单配置接口。

因此为了只能另辟蹊径实现白名单效果,你可以在过滤器的 doFilter() 中通过 req.getRequestURI() 获取请求的资源实现判断过滤,从而实现白名单效果。

下面介绍另一种方法,自定义过滤器类并继承 OncePerRequestFilter 类,将白名单配置策略配置于 shouldNotFilter() 中。

public class MyFilter2 extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 同 Filter 的 doFilter()
    }

    /**
     * 自定义白名单,返回值 true 则不会触发 doFilter()
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String[] excludes = {"/api/filter2/demo3"};
        String path = request.getRequestURI();
        return Arrays.asList(excludes).contains(path);
    }
}

二、拦截器

拦截器是 Spring 框架中的一个组件,它可以在请求处理前后和异常处理时进行预处理和后处理。拦截器和过滤器类似,但它是 Spring 框架中的一部分,可以更方便地使用 Spring 中的功能。

通过 requestresponse 这两个参数,拦截器可以获取请求的相关信息,如请求 URL 、请求参数等,并可以修改响应的相关信息,如设置响应头、设置响应编码等。

1. 业务定义

通过实现 HandlerInterceptor 接口类即可自定义拦截器功能。

其中方法参数中的 Object 代表当前请求所对应的处理器对象 (Handler),它可以是一个 Controller 方法,也可以是一个静态资源等等,具体取决于拦截器的配置和拦截器所拦截的请求路径。

@Component
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 发送请求之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Pre Handle method is Calling");
        return true;
    }

    /**
     * 发送请求之后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Post Handle method is Calling");
    }

    /**
     * 请求完成之后
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
        System.out.println("Request and Response is completed");
    }
}

2. 实例装配

完成上述拦截器的定义之后就可以禁停实现装配了,即配置拦截哪些请求。

如下设置中即所有在 /api/ 路径下的请求都会触发我们所定义的拦截器,但其中 /api/error 请求则会放行。可根据你的业务需求创建不同的拦截器针对不同的请求进行拦截,这里不作详细介绍。

其中 /* 表示当前路径,不包括子路径,而 /** 表示当前路径以及所有子路径。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        MyInterceptor myInterceptor = new MyInterceptor();

        String[] path = {"/api/**"};
        String[] excludePath = {"/api/error"};
        registry.addInterceptor(myInterceptor)
                .addPathPatterns(path)
                .excludePathPatterns(excludePath);
    }
}

3. 效果测试

新增一个简单的 Controller 接口用于测试拦截效果。

@RestController
@RequestMapping("api/interceptor")
public class StudentController {

    @GetMapping("demo1")
    public String demo1() {
        return "This is demo1.";
    }
}

完成上述配置之后启动工程,通过 Postman 请求 /api/interceptor/demo1 接口,即可看到控制台打印拦截器中配置的打印信息。

三、作用区别

1. 区别

(1) 过滤器

基于 Servlet ,通过集成 Filter 实现过滤效果,可以获取原始的 HTTP 请求与响应,但无法获取请求要访问的类与方法以及参数。

(2) 拦截器

基于 Spring MVC 提供的拦截器接口自定义实现,可以获取请求的状态和访问的类与方法,但无法获取请求参数的值。

(3) AOP切面

基于 Spring, 通过 @Aspect 注解实现,可以获取访问的类方法以及参数值,但是无法获取 HTTP 原始的请求与相应的对象。

2. 执行顺序

(1) 请求处理顺序

过滤器 -> 拦截器 -> AOP切面

(2) 报错处理顺序

AOP切面 -> controllerAdvice -> 拦截器 -> 过滤器


文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录