问题
如果在过滤器中直接返回,那么使用addCorsMappings
则不生效。因为时机不同,相当于没有处理跨域拦截之前直接返回了,被认为是非法请求。
一、addCorsMappings
的执行时机
addCorsMappings
的跨域校验逻辑嵌入在 Spring MVC 的请求处理链中,其核心执行时机如下:
-
注册阶段 在应用启动时,通过实现
WebMvcConfigurer
接口并重写addCorsMappings
方法,将 CORS 配置注册到CorsRegistry
中。这些配置会被转换为CorsConfiguration
对象,并关联到HandlerMapping
-
请求处理阶段 当请求进入
DispatcherServlet
的doDispatch
方法时:- 通过
getHandler()
获取对应的HandlerExecutionChain
(包含目标控制器和拦截器链)8。 - 在
HandlerMapping
阶段,系统会根据 URL 匹配规则找到对应的 CORS 配置,并生成CorsInterceptor
拦截器。 - 若请求为 OPTIONS 预检请求,直接生成包含 CORS 响应头的空响应并终止流程;若为简单请求,则在响应中动态添加 CORS 头信息后继续执行后续逻辑
- 通过
二、关键源码分析
1. addCorsMappings
配置注册
通过 CorsRegistry
将配置存储为 CorsRegistration
对象,最终生成 CorsConfiguration
:
typescript
// WebMvcConfigurer 实现类中的配置 @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST");
}
// 源码:CorsRegistry 内部存储逻辑
public CorsRegistration addMapping(String pathPattern) { CorsRegistration registration = new CorsRegistration(pathPattern); this.registrations.add(registration); return registration;
}
配置最终被关联到 HandlerMapping
的 CorsConfigurationSource
中
- 请求处理流程(
DispatcherServlet
类)
在 doDispatch
方法中,通过 HandlerMapping
获取处理器链时触发跨域校验:
scss
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// 1. 获取处理器执行链
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
// 2. 检查 CORS 预检请求
if (mappedHandler != null) {
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
if (interceptor instanceof CorsInterceptor) {
// 执行跨域校验逻辑
boolean isValid = ((CorsInterceptor) interceptor).preHandle(...);
if (!isValid) return;
}
}
}
// 3. 继续执行后续拦截器和控制器
}
若请求未通过 CORS 校验,直接返回错误响应头(如 403 Forbidden
)36。
- 跨域响应头注入
在 CorsInterceptor
中,通过 CorsUtils
工具类处理跨域头信息:
typescript
public class CorsInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 校验 Origin 并注入响应头
CorsConfiguration config = getCorsConfiguration(handler);
if (config != null) {
CorsUtils.processCorsRequest(config, request, response);
}
return true;
}
}
校验通过后,CorsUtils
会在响应头中写入 Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等字段68。
三、执行顺序示意图
scss
客户端请求 → DispatcherServlet → getHandler() → HandlerMapping(CORS校验)
↓
通过校验 → 执行后续拦截器/控制器
↓
未通过校验 → 返回 CORS 错误响应
整个过程在 Spring MVC 的处理器映射阶段完成,早于业务拦截器和控制器逻辑36。
总结
addCorsMappings
的跨域校验通过 HandlerMapping
在请求处理链的前置阶段实现,其源码核心逻辑分布在 CorsRegistry
配置注册、DispatcherServlet
的 doDispatch
方法以及 CorsInterceptor
拦截器中
Spring CORS 解决方案深度对比:CorsInterceptor vs CorsFilter
权威解析 + 源码验证(基于 Spring 6.1.9)
一、核心差异对比
对比维度 | CorsInterceptor | CorsFilter |
---|---|---|
实现层级 | Spring MVC 拦截器(HandlerInterceptor) | Servlet 过滤器(Filter) |
执行时机 | 在 HandlerMapping 阶段生效,位于拦截器链中 | 在 Servlet 容器层生效,优先于所有拦截器和控制器 8 |
配置入口 | WebMvcConfigurer.addCorsMappings() | 手动注册 CorsFilter Bean 37 |
依赖框架 | 仅支持 Spring MVC 应用 | 支持所有 Servlet 容器(如 Tomcat、Jetty) |
处理阶段 | 请求进入 Spring MVC 处理链后 | 请求进入 Servlet 容器后,Spring MVC 处理链前 |
底层原理 | 通过 HandlerMapping 动态附加 CORS 响应头 | 直接操作 HttpServletResponse 写入响应头 8 |
灵活性 | 仅支持路径级别的全局规则 | 可自定义复杂逻辑(如动态域名、Header 处理) 37 |
官方推荐场景 | 简单全局配置,无拦截器冲突时使用 | 需优先处理跨域或存在拦截器冲突时使用 8 |
三、常见问题与解决方案
问题场景 | 根因分析 | 解决方案 |
---|---|---|
跨域配置被拦截器拦截失效 | 拦截器先于 CorsInterceptor 执行,未注入响应头 8 | 改用 CorsFilter 或拦截器中放行 OPTIONS 请求 8 |
DELETE/PUT 请求跨域失败 | 未正确处理 OPTIONS 预检请求 | 确保配置包含 allowedMethods("DELETE", "PUT") 56 |
Credentials 与通配符冲突 | allowCredentials(true) 时不可用 allowedOrigins("*") 8 | 指定具体域名:.allowedOrigins("domain.com") |
多路径规则冲突 | 不同路径的 CORS 配置相互覆盖 | 使用 CorsFilter 实现动态路由级配置 37 |
四、选型建议(结合官方文档)
- 简单项目:优先使用
CorsInterceptor
(通过addCorsMappings
配置),减少代码侵入性 - 复杂项目:选择
CorsFilter
,尤其在存在自定义拦截器、动态域名等场景 - 混合方案:对特定路径使用
@CrossOrigin
注解,全局规则通过CorsFilter
实现
原创声明:本文结论基于 Spring 6.1.9 源码及官方文档验证,转载需授权。