PAYATHON 2026

Spring Boot 自动配置中的 CorsConfigurationSource 为何不生效?

数据阿明

问题出在 @ConditionalOnMissingBean 的类型判断上:mvcHandlerMappingIntrospector 也实现了 CorsConfigurationSource,因此条件不成立,库中的默认 Bean 没有注册。可以用 ignored = HandlerMappingIntrospector.class 排除这个框架组件,同时继续检测应用提供的 CorsConfigurationSource。

修正条件后,还需要明确多个同类型 Bean 的注入选择,避免安全配置拿到 MVC 的 introspector。

为什么默认 Bean 没有注册?

在 @Bean 方法上直接使用 @ConditionalOnMissingBean 时,默认按方法返回类型检查已有 Bean。原方法返回 CorsConfigurationSource,条件报告显示已经找到 mvcHandlerMappingIntrospector,所以该方法被跳过。ConditionalOnMissingBean 文档

HandlerMappingIntrospector 实现了 CorsConfigurationSource,用于根据请求获取 Spring MVC 中的 CORS 配置。它的存在不代表应用已经提供了与公司配置属性对应的跨域规则,但类型条件无法区分这两种用途。HandlerMappingIntrospector 文档

日志表明 Bean 注册条件未满足。此时调整已注册 Bean 的优先级,无法让被跳过的方法重新生效。

修改自动配置中的条件

保留原自动配置类的其他内容,将 CORS Bean 方法替换为下面的写法。WebMvcSecurityProperties 和 Cors 沿用问题中的项目类型。

import java.util.List;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

// 放在原 WebMvcSecurityAutoConfiguration 类中
@Bean
@Primary
@ConditionalOnMissingBean(
    value = CorsConfigurationSource.class,
    ignored = HandlerMappingIntrospector.class
)
UrlBasedCorsConfigurationSource corsConfigurationSource(
        WebMvcSecurityProperties properties) {
    Cors cors = properties.cors();

    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(cors.origins());
    configuration.setAllowedHeaders(
        cors.allowedHeaders() != null
            ? cors.allowedHeaders()
            : List.of(CorsConfiguration.ALL));
    configuration.setAllowedMethods(
        cors.allowedMethods() != null
            ? cors.allowedMethods()
            : List.of(CorsConfiguration.ALL));
    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source =
        new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

显式指定 value = CorsConfigurationSource.class 后,应用只要提供该接口的任意实现,默认配置就会退让。即使方法返回类型改成更具体的 UrlBasedCorsConfigurationSource,条件的检测范围也不会缩小。ignored 只负责从条件检查中排除 HandlerMappingIntrospector。条件注解参数说明

使用具体返回类型也方便框架在实例化前识别 Bean 类型。不过,原代码已经显式调用 .configurationSource(...),这次故障的直接原因仍是条件没有匹配。

明确安全配置注入哪个 Bean

ignored 不会删除 mvcHandlerMappingIntrospector,也不会把它排除在依赖注入之外。默认配置注册成功后,容器里仍可能有两个 CorsConfigurationSource 候选。

上面的 @Primary 让默认配置成为单值注入的首选。只要候选中恰好有一个 primary Bean,Spring 就会优先注入它。Spring 的 @Primary 说明

应用提供自定义配置时,也可以将自己的 Bean 标记为 @Primary。下面是一个独立的应用配置示例,域名和允许的方法需要按实际需求调整:

@Bean
@Primary
UrlBasedCorsConfigurationSource applicationCorsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(List.of("https://app.example.com"));
    configuration.setAllowedMethods(List.of("GET", "POST"));
    configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source =
        new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

在正常通过 Spring Boot 自动配置机制加载的前提下,应用配置中的这个 Bean 会使库的默认 Bean 退让。这里按类型检测,因此它不必与默认 Bean 同名,也无需开启同名 Bean 覆盖。

原来的安全链可以继续显式传入注入的对象:

.cors(configurer ->
    configurer.configurationSource(corsConfigurationSource))

这种写法直接指定当前 SecurityFilterChain 使用的配置源。如果应用为不同安全链定义了多个配置源,可以通过 @Qualifier 分别选择,再传给各自的 .configurationSource(...)。Spring Security CORS 配置说明

为什么调整顺序没有解决问题?

@AutoConfigureBefore(SecurityAutoConfiguration.class) 只声明与指定自动配置类之间的先后关系,不能保证条件判断时不存在 MVC 注册的 Bean。自动配置顺序影响 Bean 定义的处理顺序,也不等同于 Bean 的实例化顺序。AutoConfigureBefore 文档

既然条件失败的原因已经明确,应先修正检测范围。单独添加 @Order(HIGHEST_PRECEDENCE) 无法改变 HandlerMappingIntrospector 实现了该接口这一事实。

如何确认修正生效?

分别检查两种启动场景:

  • 应用没有自定义配置源时,默认 CORS Bean 应当注册,条件报告不再因 mvcHandlerMappingIntrospector 而拒绝它。
  • 应用提供自定义配置源时,默认 Bean 应当退让,安全链注入应用选定的配置源。

随后使用带 Origin 和 Access-Control-Request-Method 的 OPTIONS 预检请求检查实际 CORS 响应。Bean 注册成功只能证明条件问题已解决,具体跨域行为还取决于来源、方法和请求头配置。

原代码设置了 allowCredentials(true),此时 allowedOrigins 不能包含 "*"。应配置可信的具体来源;如确需来源模式匹配,再按需求使用 allowedOriginPatterns。CorsConfiguration 文档

备注:内容仅供参考。