Spring Boot 3.x+Azure AAD升级后CORS失效,原2.6版本正常
Spring Boot 3.x + Azure AAD 5.4 升级后CORS失效问题排查
我们的应用需自定义OIDCUserService、OIDCUser对象,并结合Spring配置Azure AAD安全策略。Spring Boot 2.6版本下配置可正常运行,但升级至Spring Boot 3.x与Azure 5.4后,改用新配置类重构代码,CORS功能失效,已耗时两周仍未解决,现排查配置问题。
旧版本(Spring Boot 2.6)正常配置代码
@EnableWebSecurity class AADServerConfig { @Order(1) @Configuration static class ApiWebSecurityConfigurationAdapter extends AadResourceServerWebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http) http .cors() .and() .csrf().disable() .antMatcher("/api/**") .authorizeRequests().anyRequest().authenticated() http .headers() .frameOptions() .disable() .httpStrictTransportSecurity() .disable() } } @Configuration class HtmlWebSecurityConfigurerAdapter extends AadWebSecurityConfigurerAdapter { @Autowired private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; @Autowired private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; @Autowired private OAuth2HttpCookieAuthorizationRequestRepository oAuth2HttpCookieAuthorizationRequestRepository; @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http) http .oauth2Login() .authorizationEndpoint() .authorizationRequestRepository(oAuth2HttpCookieAuthorizationRequestRepository) .and() .successHandler(oAuth2AuthenticationSuccessHandler) .failureHandler(oAuth2AuthenticationFailureHandler) http .cors() .and() .csrf().disable() .authorizeRequests() .antMatchers("/", "/login", "/*.js", "/*.css", "/token", "/actuator/**", "/actuator/prometheus", "/version/**", "/docusign/events/**").permitAll() .anyRequest().authenticated() http .headers() .frameOptions() .disable() .httpStrictTransportSecurity() .disable() } } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration cors = new CorsConfiguration() cors.setAllowedOrigins([ "http://localhost:3000", "http://localhost:3001", "http://localhost:8080", "https://localhost:8080", "https://dev-local.panoram.co" ]) cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "FETCH", "PATCH")) cors.setAllowCredentials(true) UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource() source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues()) return source } }
升级后(Spring Boot 3.x + Azure 5.4)重构代码(CORS失效)
@EnableWebSecurity class AADServerConfig { @Order(1) @Configuration //com.azure.spring.cloud.autoconfigure.aad.AadResourceServerWebSecurityConfigurerAdapter class ApiWebSecurityConfigurationAdapter extends AadResourceServerHttpSecurityConfigurer { @Override void configure(HttpSecurity http) throws Exception { super.configure(http) http .cors(corsCustomizer -> { corsCustomizer.configurationSource(corsConfigurationSource()) }) .csrf(csrfConfig -> { csrfConfig.disable() }) .securityMatcher("/api/**") .authorizeHttpRequests {}(requestsCustomizer -> { requestsCustomizer.anyRequest().authenticated() }) http .headers(header -> { header.frameOptions {frameOptions -> { frameOptions.disable() }} header.httpStrictTransportSecurity {transportConfig -> transportConfig.disable() } }) } } @Configuration class HtmlWebSecurityConfigurerAdapter extends AadWebApplicationHttpSecurityConfigurer { @Autowired private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; @Autowired private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; @Autowired private OAuth2HttpCookieAuthorizationRequestRepository oAuth2HttpCookieAuthorizationRequestRepository; @Override void configure(HttpSecurity http) throws Exception { super.configure(http) http .oauth2Login(customizer -> { customizer.authorizationEndpoint {endpointConfig -> { endpointConfig.authorizationRequestRepository(oAuth2HttpCookieAuthorizationRequestRepository) }} customizer.successHandler {oAuth2AuthenticationSuccessHandler} customizer.failureHandler {oAuth2AuthenticationFailureHandler} }) http .cors(corsCustomizer -> { corsCustomizer.configurationSource(corsConfigurationSource()) }) .csrf(csrfConfig -> { csrfConfig.disable() }) .authorizeHttpRequests(authz ->{ authz.requestMatchers("/", "/login", "/*.js", "/*.css", "/token", "/actuator/**", "/actuator/prometheus", "/version/**", "/docusign/events/**").permitAll() .anyRequest().authenticated() }) http.headers(header -> { header.frameOptions {frameOptions -> { frameOptions.disable() }} header.httpStrictTransportSecurity {transportConfig -> transportConfig.disable() } }) } } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration() configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "https://localhost:3000", "http://localhost:3001", "https://localhost:3001", "https://localhost:5001", "http://localhost:5001", "http://localhost:8080", "https://localhost:8080", "https://dev-local.panoram.co")) configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "FETCH", "PATCH")) configuration.setAllowCredentials(true) CorsConfigurationSource source = new UrlBasedCorsConfigurationSource() source.registerCorsConfiguration("/**", configuration.applyPermitDefaultValues()) return source } }
浏览器控制台错误日志

配置问题分析与修复方案
核心问题点
配置类继承方式错误:
AadResourceServerHttpSecurityConfigurer和AadWebApplicationHttpSecurityConfigurer是配置器工具类,而非WebSecurityConfigurerAdapter的替代类,不能直接继承作为配置类使用。Spring Boot 3.x中推荐基于SecurityFilterChainBean的组件式配置。SecurityMatcher顺序错误:在新配置中,
.securityMatcher("/api/**")放在CORS、CSRF配置之后,导致CORS过滤器无法匹配到目标请求,规则不生效。CORS配置实例重复创建:两个配置类中手动调用
corsConfigurationSource()创建新实例,未复用容器中已注册的Bean,导致配置不统一。
修正后的配置代码
@EnableWebSecurity class AADServerConfig { @Autowired private CorsConfigurationSource corsConfigurationSource; @Order(1) @Configuration static class ApiWebSecurityConfiguration { @Autowired private CorsConfigurationSource corsConfigurationSource; @Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { // 应用Azure AAD资源服务器配置器 http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer()) .and() .securityMatcher("/api/**") // 优先设置请求匹配范围 .cors(cors -> cors.configurationSource(corsConfigurationSource)) .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz.anyRequest().authenticated()) .headers(headers -> headers .frameOptions(frame -> frame.disable()) .httpStrictTransportSecurity(hsts -> hsts.disable())); return http.build(); } } @Configuration static class HtmlWebSecurityConfiguration { @Autowired private CorsConfigurationSource corsConfigurationSource; @Autowired private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; @Autowired private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; @Autowired private OAuth2HttpCookieAuthorizationRequestRepository oAuth2HttpCookieAuthorizationRequestRepository; @Bean public SecurityFilterChain htmlSecurityFilterChain(HttpSecurity http) throws Exception { // 应用Azure AAD Web应用配置器 http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication()) .and() .cors(cors -> cors.configurationSource(corsConfigurationSource)) .csrf(csrf -> csrf.disable()) .oauth2Login(oauth2 -> oauth2 .authorizationEndpoint(endpoint -> endpoint .authorizationRequestRepository(oAuth2HttpCookieAuthorizationRequestRepository)) .successHandler(oAuth2AuthenticationSuccessHandler) .failureHandler(oAuth2AuthenticationFailureHandler)) .authorizeHttpRequests(authz -> authz .requestMatchers("/", "/login", "/*.js", "/*.css", "/token", "/actuator/**", "/actuator/prometheus", "/version/**", "/docusign/events/**").permitAll() .anyRequest().authenticated()) .headers(headers -> headers .frameOptions(frame -> frame.disable()) .httpStrictTransportSecurity(hsts -> hsts.disable())); return http.build(); } } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList( "http://localhost:3000", "https://localhost:3000", "http://localhost:3001", "https://localhost:3001", "https://localhost:5001", "http://localhost:5001", "http://localhost:8080", "https://localhost:8080", "https://dev-local.panoram.co" )); configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "FETCH", "PATCH")); configuration.setAllowCredentials(true); configuration.applyPermitDefaultValues(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
关键修正说明
- 替换继承逻辑:通过
http.apply()方法应用Azure的配置器,符合Spring Boot 3.x的安全配置规范。 - 调整匹配器顺序:将
securityMatcher移至配置最前端,确保CORS等过滤器优先作用于目标请求。 - 复用CORS配置Bean:注入容器中已定义的
CorsConfigurationSource,保证配置一致性。 - 返回SecurityFilterChain:每个配置类构建独立的
SecurityFilterChainBean,Spring按@Order优先级处理不同路径的请求。
内容的提问来源于stack exchange,提问作者bytor99999
相关产品推荐
相关产品推荐

