SpringBoot过滤器、拦截器与跨域处理核心实践
2026/9/3 6:21:25 网站建设 项目流程

文章目录

    • 一、过滤器与拦截器核心概念及层级区别
      • 1.1 过滤器(Filter)
      • 1.2 拦截器(Interceptor)
      • 1.3 核心差异对比表
    • 二、过滤器实战开发
      • 2.1 全局通用过滤器完整代码
    • 三、拦截器实战开发
      • 3.1 登录权限拦截器实现
      • 3.2 拦截器注册配置类
    • 四、跨域问题两种主流解决方案
      • 4.1 方案一:Spring全局跨域配置(推荐)
      • 4.2 方案二:自定义跨域过滤器
    • 五、请求全链路执行顺序与生产避坑指南
      • 5.1 完整请求执行链路
      • 5.2 高频生产避坑要点
    • 六、生产级高阶实战干货
      • 6.1 高阶一:Spring托管过滤器(解决Bean注入为空)
      • 6.2 高阶二:请求体重复读取解决方案
      • 6.3 高阶三:多环境动态跨域配置(生产级安全方案)
    • 八、高频面试题集锦
    • 九、总结

在SpringBoot前后端分离项目开发中,过滤器、拦截器是实现请求预处理、权限校验、接口日志统计、参数统一处理的核心组件,跨域处理则是项目上线必须解决的基础问题。三者执行层级不同、适用场景各异,也是日常开发中极易混淆的知识点。

一、过滤器与拦截器核心概念及层级区别

过滤器(Filter)与拦截器(Interceptor)均用于拦截HTTP请求,实现请求统一预处理,但二者底层规范、容器依赖、拦截范围、执行时机完全不同,是区分业务使用场景的核心依据。

1.1 过滤器(Filter)

过滤器是Servlet原生规范组件,不依赖Spring框架,由Tomcat等Servlet容器负责初始化与管理。其拦截层级最高,可拦截容器接收的所有HTTP请求,包括静态资源、非法请求、未映射接口请求,执行时机在Spring MVC核心分发器DispatcherServlet之前。

核心特点:全局拦截、优先级最高、无Spring容器依赖、性能损耗低;默认无法注入Spring Bean,适合通用底层处理逻辑。

1.2 拦截器(Interceptor)

拦截器是Spring MVC专属组件,由Spring容器初始化管理,仅拦截DispatcherServlet分发的Controller接口请求,不拦截静态资源、非法映射请求。执行时机位于过滤器之后、Controller方法执行前后。

核心特点:依托Spring容器、支持依赖注入、可精准获取接口方法信息、仅拦截业务接口;专注业务层级的请求处理逻辑。

1.3 核心差异对比表

对比维度过滤器(Filter)拦截器(Interceptor)
底层依赖Servlet规范、容器管理Spring MVC框架、Spring容器管理
拦截范围全部HTTP请求(接口、静态资源)仅Controller映射接口请求
执行顺序DispatcherServlet 执行之前DispatcherServlet 执行之后
依赖注入默认不支持 @Autowired完全支持Spring Bean注入
核心场景跨域处理、全局编码、请求过滤权限校验、登录拦截、接口日志、参数预处理

二、过滤器实战开发

自定义过滤器需实现Servlet的Filter接口,重写初始化、过滤、销毁三个核心方法。可通过注解快速注册,适用于全局统一编码、请求日志、基础过滤等通用场景。

2.1 全局通用过滤器完整代码

importjakarta.servlet.*;importjakarta.servlet.http.HttpServletRequest;importlombok.extern.slf4j.Slf4j;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;importjava.io.IOException;/** * 全局自定义过滤器 * 实现全局编码统一、请求基础信息打印 */@Slf4j@Component@Order(1)// 数值越小,过滤器执行优先级越高publicclassGlobalFilterimplementsFilter{/** * 容器启动时执行一次,用于初始化资源 */@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{log.info("全局过滤器初始化完成");}/** * 核心过滤逻辑,每次请求都会执行 */@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{// 统一全局编码request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");// 打印请求基础信息HttpServletRequesthttpRequest=(HttpServletRequest)request;StringrequestUri=httpRequest.getRequestURI();Stringmethod=httpRequest.getMethod();log.info("过滤器拦截请求,地址:{},请求方式:{}",requestUri,method);// 放行请求,必须执行,否则请求阻塞chain.doFilter(request,response);}/** * 项目关闭时执行一次,用于资源释放 */@Overridepublicvoiddestroy(){log.info("全局过滤器销毁,释放资源");}}

代码说明:通过@Component将过滤器交由Spring管理,@Order控制多过滤器执行优先级,doFilter为核心方法,必须调用chain.doFilter()放行请求,否则请求会直接阻塞。

三、拦截器实战开发

自定义拦截器需实现Spring的HandlerInterceptor接口,包含请求前置、后置、完成后三个拦截阶段。拦截器无法直接生效,必须通过WebMvc配置类注册并配置拦截规则。

3.1 登录权限拦截器实现

importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;/** * 全局接口拦截器 * 实现Token登录校验、接口访问日志记录 */@Slf4j@ComponentpublicclassGlobalInterceptorimplementsHandlerInterceptor{/** * Controller方法执行前执行 * return true 放行请求,return false 拦截请求 */@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){// 仅拦截接口方法,放行静态资源、视图等非业务请求if(!(handlerinstanceofHandlerMethod)){returntrue;}HandlerMethodhandlerMethod=(HandlerMethod)handler;log.info("拦截器捕获接口请求:{}",handlerMethod.getMethod().getName());// 模拟登录Token校验逻辑Stringtoken=request.getHeader("token");if(token==null||token.isEmpty()){log.warn("接口请求未携带Token,拦截访问");response.setStatus(401);returnfalse;}returntrue;}/** * Controller执行完成、视图渲染前执行 */@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView){log.info("接口业务方法执行完成,准备响应数据");}/** * 整个请求流程结束,资源释放、最终日志记录 */@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){log.info("本次请求流程完全结束");}}

3.2 拦截器注册配置类

importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * 拦截器注册配置类 * 统一配置拦截路径、放行路径 */@ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{privatefinalGlobalInterceptorglobalInterceptor;// 构造器注入自定义拦截器publicInterceptorConfig(GlobalInterceptorglobalInterceptor){this.globalInterceptor=globalInterceptor;}@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(globalInterceptor).addPathPatterns("/**")// 拦截所有接口请求.excludePathPatterns("/login","/register","/static/**");// 放行登录、注册、静态资源}}

四、跨域问题两种主流解决方案

跨域问题由浏览器同源策略限制导致,前后端协议、域名、端口任一不一致即触发跨域拦截。核心关键点:浏览器OPTIONS预检请求不会进入Spring MVC拦截器,因此跨域无法通过拦截器实现,仅能通过全局配置或过滤器解决。

4.1 方案一:Spring全局跨域配置(推荐)

配置集中、维护简单、无代码侵入,适配绝大多数前后端分离项目,是企业开发首选方案。

importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.CorsRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * 全局统一跨域配置 */@ConfigurationpublicclassCorsConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddCorsMappings(CorsRegistryregistry){registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET","POST","PUT","DELETE","OPTIONS").allowedHeaders("*").allowCredentials(true).maxAge(3600);}}

4.2 方案二:自定义跨域过滤器

通过过滤器手动设置跨域响应头,优先拦截并放行OPTIONS预检请求,适配特殊自定义场景。

importjakarta.servlet.*;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importlombok.extern.slf4j.Slf4j;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;importjava.io.IOException;/** * 自定义跨域过滤器 * 优先级最高,提前处理跨域预检请求 */@Slf4j@Component@Order(0)publicclassCorsFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestreq=(HttpServletRequest)request;HttpServletResponseres=(HttpServletResponse)response;// 设置跨域响应头res.setHeader("Access-Control-Allow-Origin","*");res.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS");res.setHeader("Access-Control-Allow-Headers","*");res.setHeader("Access-Control-Allow-Credentials","true");res.setHeader("Access-Control-Max-Age","3600");// 直接放行OPTIONS预检请求if("OPTIONS".equalsIgnoreCase(req.getMethod())){res.setStatus(200);return;}chain.doFilter(request,response);}}

五、请求全链路执行顺序与生产避坑指南

5.1 完整请求执行链路

浏览器发起请求 → 跨域过滤器预处理 → 全局编码过滤器 → DispatcherServlet分发请求 → 拦截器preHandle前置拦截 → Controller业务执行 → 拦截器postHandle后置处理 → 视图渲染 → 拦截器afterCompletion收尾 → 响应返回浏览器

5.2 高频生产避坑要点

  1. 跨域不可用拦截器实现:OPTIONS预检请求不进入Spring MVC拦截器链路,拦截器配置跨域完全无效。

  2. 原生Filter无法注入Bean:普通Filter由Tomcat容器初始化,早于Spring容器启动,直接@Autowired会出现Bean为null。

  3. 跨域双方案冲突:全局Cors配置与自定义CorsFilter不能同时启用,会导致响应头覆盖、跨域报错。

  4. 通配符与Cookie互斥:开启allowCredentials(true)时,禁止使用*通配符域名,浏览器会直接拦截跨域请求。

  5. 静态资源拦截问题:未配置静态资源放行时,拦截器会拦截css、js、图片等资源,导致页面样式失效。

  6. 请求体仅可读一次:原生HttpServletRequest输入流只能读取一次,拦截器读取Body后,Controller会获取空参数。

  7. 优先级底层原理:Filter属于Web底层组件,始终在DispatcherServlet之前执行;Interceptor属于Spring MVC组件,在DispatcherServlet内部执行,因此过滤器优先级天然高于拦截器。

六、生产级高阶实战干货

本章聚焦企业开发刚需高阶解决方案,解决过滤器Bean注入、请求体重复读取、多环境动态跨域三大生产级难题,所有代码可直接上线使用。

6.1 高阶一:Spring托管过滤器(解决Bean注入为空)

通过FilterRegistrationBean将过滤器交由Spring容器管理,脱离Tomcat原生管理机制,完美支持Service、配置类等Bean注入,适配复杂业务过滤场景。

importjakarta.servlet.Filter;importjakarta.servlet.http.HttpServletRequest;importlombok.extern.slf4j.Slf4j;importorg.springframework.boot.web.servlet.FilterRegistrationBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.annotation.Order;/** * Spring托管过滤器配置 * 支持Spring Bean依赖注入 */@Slf4j@ConfigurationpublicclassSpringManagedFilterConfig{// 可直接注入业务Service// private final UserService userService;// public SpringManagedFilterConfig(UserService userService) {this.userService = userService;}@Bean@Order(1)publicFilterRegistrationBean<Filter>businessFilter(){FilterRegistrationBean<Filter>registrationBean=newFilterRegistrationBean<>();registrationBean.setFilter((request,response,chain)->{HttpServletRequesthttpRequest=(HttpServletRequest)request;log.info("Spring托管过滤器执行,可正常调用业务Bean");// 可实现黑名单拦截、权限二次校验等复杂逻辑chain.doFilter(request,response);});registrationBean.addUrlPatterns("/*");returnregistrationBean;}}

6.2 高阶二:请求体重复读取解决方案

自定义请求包装类缓存请求Body,解决拦截器读取JSON参数后Controller参数为空的问题,是接口日志、参数校验的通用标准方案。

importjakarta.servlet.ReadListener;importjakarta.servlet.ServletInputStream;importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletRequestWrapper;importlombok.extern.slf4j.Slf4j;importjava.io.*;/** * 可重复读取请求体的包装类 */@Slf4jpublicclassRepeatBodyRequestWrapperextendsHttpServletRequestWrapper{privatefinalbyte[]body;publicRepeatBodyRequestWrapper(HttpServletRequestrequest)throwsIOException{super(request);body=request.getInputStream().readAllBytes();}@OverridepublicServletInputStreamgetInputStream(){returnnewByteArrayServletInputStream(body);}@OverridepublicBufferedReadergetReader(){returnnewBufferedReader(newInputStreamReader(getInputStream()));}privatestaticclassByteArrayServletInputStreamextendsServletInputStream{privatefinalByteArrayInputStreaminputStream;publicByteArrayServletInputStream(byte[]bytes){this.inputStream=newByteArrayInputStream(bytes);}@OverridepublicbooleanisFinished(){returninputStream.available()==0;}@OverridepublicbooleanisReady(){returntrue;}@OverridepublicvoidsetReadListener(ReadListenerreadListener){}@Overridepublicintread(){returninputStream.read();}}}

请求包装过滤器,全局生效:

importjakarta.servlet.*;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;importjava.io.IOException;/** * 全局请求体包装过滤器 * 实现Body数据可重复读取 */@Component@Order(-100)publicclassRepeatBodyFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{RepeatBodyRequestWrapperrequestWrapper=newRepeatBodyRequestWrapper((HttpServletRequest)request);chain.doFilter(requestWrapper,response);}}

6.3 高阶三:多环境动态跨域配置(生产级安全方案)

摒弃通配符域名,通过配置文件动态读取跨域域名,支持开发、测试、生产多环境隔离,兼顾安全性与灵活性,符合企业上线规范。

importorg.springframework.context.annotation.Configuration;importorg.springframework.core.env.Environment;importorg.springframework.web.servlet.config.annotation.CorsRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * 生产级动态跨域配置 * 多域名、多环境隔离,安全合规 */@ConfigurationpublicclassProductionCorsConfigimplementsWebMvcConfigurer{privatefinalEnvironmentenvironment;publicProductionCorsConfig(Environmentenvironment){this.environment=environment;}@OverridepublicvoidaddCorsMappings(CorsRegistryregistry){// 从配置文件读取可跨域域名StringallowOrigin=environment.getProperty("system.cors.allow-origin","http://localhost:8080,http://127.0.0.1:8080");String[]origins=allowOrigin.split(",");registry.addMapping("/**").allowedOrigins(origins).allowedMethods("GET","POST","PUT","DELETE","OPTIONS").allowedHeaders("*").allowCredentials(true).maxAge(3600);}}

yml配置文件:

# 跨域域名配置system:cors:allow-origin:http://localhost:8080,http://test.xxx.com,https://www.xxx.com

八、高频面试题集锦

1. 说一下 Filter 和 Interceptor 的区别?

① 底层不同:Filter是Servlet原生组件,由Tomcat容器管理;Interceptor是Spring MVC组件,由Spring容器管理。
② 执行时机不同:Filter在DispatcherServlet之前执行;Interceptor在DispatcherServlet之后、Controller前后执行。
③ 拦截范围不同:Filter拦截所有请求,包含静态资源、非法请求;Interceptor只拦截Controller映射的接口请求。
④ 能力不同:原生Filter无法注入Bean,Interceptor完美支持Spring依赖注入。
⑤ 适用场景不同:Filter做底层通用处理,跨域、编码、限流;Interceptor做业务层处理,登录校验、权限拦截、接口日志。

2. 为什么拦截器不能处理跨域?

浏览器跨域复杂请求会先发OPTIONS预检请求,该请求只会经过Tomcat过滤器链路,不会进入Spring MVC的DispatcherServlet,因此不会走到拦截器逻辑。所以跨域只能通过过滤器或全局Cors配置处理,拦截器配置跨域完全无效。

3. 过滤器、拦截器、Controller、AOP 执行顺序?

Filter过滤器 → DispatcherServlet → Interceptor前置preHandle → AOP前置通知 → Controller方法执行 → AOP后置通知 → Interceptor后置postHandle → 视图渲染 → Interceptor最终afterCompletion。

4. 自定义Filter为什么@Autowired注入Bean为null?怎么解决?

原生Filter由Tomcat容器初始化,初始化时机早于Spring容器,此时Spring Bean还未创建,导致注入为空。

解决方案:使用 FilterRegistrationBean 注册过滤器,将过滤器交由Spring容器管理,即可正常注入业务Bean。

5. 跨域配置 allowCredentials=true 为什么不能用 * 通配符?

这是浏览器的安全策略限制。开启允许携带Cookie后,若使用通配符匹配所有域名,会存在Cookie劫持风险,浏览器会直接拦截请求并报错。生产环境必须配置具体的可信域名列表,不能使用*。

6. 需要做全局接口Token校验,选过滤器还是拦截器?为什么?

优先选拦截器。Token校验属于业务层权限逻辑,需要注入用户、权限相关业务Bean,拦截器天然支持Spring依赖注入;且拦截器可精准拦截接口、放行静态资源,适配业务场景,比过滤器更灵活。

7. 说下 FilterRegistrationBean 和 @Component 注册过滤器的区别?

① @Component注册的过滤器由Spring自动托管,但底层仍依托Tomcat初始化,默认无法精准控制过滤规则;
② FilterRegistrationBean是手动注册方式,可精准控制过滤路径、优先级、是否启用,最重要的是可以让过滤器完全由Spring容器管理,解决Bean注入为空问题,适合复杂生产场景。

九、总结

过滤器专注Web底层全局请求处理,性能优异,适合跨域、编码统一等基础通用逻辑;拦截器专注Spring业务层请求处理,支持依赖注入,适合权限校验、接口日志、参数预处理等业务逻辑。二者分层执行、各司其职,是SpringBoot请求链路处理的核心基石。


文章结束,喜欢就给个一键三连吧,你的肯定是我最大的动力,点赞上一千我就是脑瘫也出下章

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询