You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot 3 + Spring Security 6下POST /login接口CORS预检请求返回403“Invalid CORS request”问题求助

Spring Boot 3 + Spring Security 6下POST /login接口CORS预检请求返回403“Invalid CORS request”问题求助

我之前把Spring Boot后端部署到Azure App Service时,正好碰到过一模一样的CORS预检403问题!结合你的配置和curl输出,帮你梳理几个核心排查点和解决办法:

1. 先修正日志打印,确认配置是否真的生效

你代码里打印的是config.getAllowedOrigins(),但实际用的是setAllowedOriginPatterns()配置的——这两个是完全独立的属性!getAllowedOrigins()返回的是旧API的配置值,而你用的是新的pattern配置,这条日志根本没打到点上,会误导你。

赶紧把日志改成:

System.out.println("🔍 CORS config initialized with allowedOriginPatterns=" + config.getAllowedOriginPatterns());

重启后端后,确认日志里能看到你配置的两个origin(localhost和Azure静态Web Apps的URL)。

2. 检查Azure App Service的全局CORS配置(90%概率是这个坑!)

Azure App Service本身有一层网关级别的CORS配置,如果你在Azure门户里配了CORS规则,会和Spring Boot的CORS配置冲突——预检请求会先被Azure网关拦截,直接返回403,根本到不了你的Spring Boot应用。

排查步骤:

  • 登录Azure门户,找到你的App Service实例
  • 左侧菜单找到「CORS」选项卡
  • 看看这里是否配置了Origin列表:
    • 如果配置了,要么确保和Spring Boot里的规则完全一致(包括协议、域名、端口)
    • 要么直接清空所有配置,把CORS的控制权完全交给Spring Boot(更推荐,避免双层配置冲突)

这一步是我当时踩的最大的坑,Azure的CORS优先级比应用级的高!

3. 确保Spring Security的CORS过滤器顺序正确

在Spring Security 6中,CORS过滤器必须处于过滤器链的最优先级,否则OPTIONS请求可能被其他安全过滤器提前拦截。

你的SecurityFilterChain配置顺序是对的(先关csrf,再开cors),但可以显式给CORS相关的Bean加上最高优先级,确保万无一失:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public CorsFilter corsFilter(CorsConfigurationSource corsConfigurationSource) {
    return new CorsFilter(corsConfigurationSource);
}

同时,保持SecurityFilterChain里的.cors(cors -> cors.configurationSource(corsConfigurationSource()))配置不变。

4. 给你一个经过验证的完整配置示例

我现在生产环境用的就是这套配置,适配Spring Boot 3.2 + Spring Security 6.2,部署在Azure App Service上完全正常:

CORS配置Bean

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    // 用allowedOriginPatterns支持通配符或具体域名,推荐用pattern而非固定origin
    config.setAllowedOriginPatterns(List.of(
        "http://localhost:5173",
        "https://lively-field-0cb114f0f.3.azurestaticapps.net"
    ));
    // 允许所有需要的HTTP方法,必须包含OPTIONS(预检请求用)
    config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
    // 允许所有请求头,或者按需指定
    config.setAllowedHeaders(List.of("*"));
    // 暴露前端需要的响应头(比如Authorization)
    config.setExposedHeaders(List.of("Authorization", "Location"));
    // 如果前端需要带Cookie或认证信息,必须设为true
    config.setAllowCredentials(true);
    // 预检请求的缓存时间,减少OPTIONS请求次数
    config.setMaxAge(3600L);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    // 对所有路径应用CORS规则
    source.registerCorsConfiguration("/**", config);
    
    // 打印正确的配置信息
    System.out.println("✅ CORS配置加载完成,允许的Origin Patterns: " + config.getAllowedOriginPatterns());
    return source;
}

SecurityFilterChain配置

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        // 先配置CORS,确保优先级最高
        .cors(cors -> cors.configurationSource(corsConfigurationSource()))
        // 前后端分离关闭CSRF
        .csrf(csrf -> csrf.disable())
        // 无状态会话
        .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        // 异常处理配置
        .exceptionHandling(e -> e
            .authenticationEntryPoint((req, res, ex) -> res.sendError(HttpServletResponse.SC_UNAUTHORIZED))
            .accessDeniedHandler((req, res, ex) -> res.sendError(HttpServletResponse.SC_FORBIDDEN))
        );
    return http.build();
}

5. 最后用curl调试确认

用带-v参数的curl命令,看完整的请求头和响应头,确认Origin是否正确传递:

curl -v -X OPTIONS "https://math-morph-backend-qa-scotland-cqdchmerbueeathy.westus-01.azurewebsites.net/vpedtech/auth/login" \
-H "Origin: https://lively-field-0cb114f0f.3.azurestaticapps.net" \
-H "Access-Control-Request-Method: POST"

如果响应头里有Access-Control-Allow-Origin,并且值和你的前端Origin一致,说明配置生效了;如果还是403,优先检查Azure的CORS配置是否冲突!

如果还是有问题,可以把调试的curl完整输出贴出来,我再帮你排查~

火山引擎 最新活动