Spring Boot与Spring的跨域解决方案
发布时间:2025-07-29 00:07       
Spring Boot 与 Spring Security 的跨域解决方案详解 🌐
在前后端分离架构中,跨域资源共享(CORS)是必须解决的问题。Spring Boot 应用整合 Spring Security 后,其强大的默认安全机制会拦截跨域请求,需要显式配置才能放行。以下是几种经过验证、符合当前最佳实践的解决方案(基于 Spring Boot 3.x / Spring Security 6.x+):
🔐 方案一:使用 CorsFilter
注册全局 CORS 配置 (推荐)
原理: 在 Spring Security 过滤器链之前插入 CORS 过滤器,优先处理 OPTIONS 预检请求。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.List;
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
// 1. 创建 CORS 配置源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 2. 创建 CORS 配置对象 (可针对不同路径配置多个)
CorsConfiguration config = new CorsConfiguration();
// 3. 配置具体规则 (按需调整)
config.setAllowCredentials(true); // 允许携带凭证 (如 cookies)
config.setAllowedOriginPatterns(List.of("https://*.yourdomain.com", "http://localhost:[*]")); // 使用模式匹配更安全灵活
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); // 允许的 HTTP 方法
config.setAllowedHeaders(List.of("*")); // 允许所有请求头 (生产环境建议细化)
config.setExposedHeaders(List.of("Authorization", "Cache-Control", "Content-Type")); // 允许前端获取的响应头
config.setMaxAge(3600L); // 预检请求缓存时间 (秒)
// 4. 注册配置:应用到所有路径
source.registerCorsConfiguration("/**", config);
// 5. 返回 CorsFilter 实例
return new CorsFilter(source);
}
}
关键点:
setAllowedOriginPatterns()
比旧的setAllowedOrigins()
更灵活,支持通配符模式。setAllowCredentials(true)
与setAllowedOriginPatterns
需配合使用,不能为*
。- Spring Security 会自动利用此过滤器处理 CORS。
🧩 方案二:在 SecurityFilterChain
中配置 CORS
原理: 在 Spring Security 的配置中显式启用 CORS 并指定配置源。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// 🔧 启用 CORS 并使用自定义配置源
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// ... 其他安全配置 (如 CSRF、授权规则、认证管理等) ...
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(...) // 或其他登录配置
.httpBasic(...);
return http.build();
}
// 🛠 定义 CORS 配置源 Bean
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOriginPatterns(List.of("https://*.trusted.com", "http://localhost:3000"));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setExposedHeaders(List.of("X-Custom-Header"));
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
关键点:
- 在
HttpSecurity.cors()
中指定自定义的CorsConfigurationSource
Bean。 - 配置方式与
CorsFilter
方案中的CorsConfiguration
相同。 - 此方案明确将 CORS 配置集成在安全配置中,逻辑更集中。
⚠ 方案三:使用 @CrossOrigin
注解 (适用于细粒度控制)
原理: 在单个 Controller 或方法上添加注解,仅作用于特定端点。
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
// 🎯 可加在类级别 (对该类所有方法生效)
@CrossOrigin(
originPatterns = {"https://api.example.com", "http://localhost:4200"},
allowCredentials = "true",
maxAge = 1800
)
public class MyApiController {
// 🎯 也可加在方法级别 (覆盖类级别配置)
@CrossOrigin(originPatterns = "http://specific.client:8080")
@GetMapping("/special")
public String specialEndpoint() {
return "Special response";
}
@GetMapping("/general")
public String generalEndpoint() {
return "General response";
}
}
适用场景 & 注意:
- 优点: 控制粒度最细,可为不同 API 设置不同规则。
- 缺点:
- 配置分散,不易全局管理维护。
- 不能单独解决 Spring Security 的拦截问题! 如果启用了 Spring Security,必须同时配合方案一或方案二的全局配置。因为 Spring Security 的过滤器链会在
@CrossOrigin
注解生效前拦截 OPTIONS 预检请求。全局配置确保了预检请求能通过安全层。 - 通常用于在全局配置基础上,对个别端点做特殊化处理。
🧪 验证与调试工具
-
浏览器开发者工具 (Network Tab): 检查请求/响应的
Origin
,Access-Control-*
相关头信息,查看预检请求 (OPTIONS
) 是否成功(状态码 200)及响应头是否正确。 -
curl
命令: 模拟预检请求:curl -i -X OPTIONS http://your-springboot-api:8080/your-endpoint \-H "Origin: http://your-frontend-origin:3000" \-H "Access-Control-Request-Method: POST" \-H "Access-Control-Request-Headers: content-type, authorization"
检查响应头中是否包含正确的
Access-Control-Allow-Origin
,Access-Control-Allow-Methods
,Access-Control-Allow-Headers
等。
🔒 最佳实践与安全建议
- 优先选择方案一 (
CorsFilter
) 或方案二 (SecurityFilterChain
中配置): 确保全局生效且能正确处理预检请求。 - 严格限制
allowedOriginPatterns
: 避免使用"*"
,尤其是在allowCredentials=true
时。明确列出信任的前端域名或使用可管理的模式 (如https://*.yourcompany.com
)。生产环境务必收紧! - 按需开放方法和头信息: 避免
setAllowedMethods(List.of("*"))
和setAllowedHeaders(List.of("*"))
。只开放前端实际需要使用的 HTTP 方法和请求头。例如:List.of("GET", "POST", "Content-Type", "Authorization")
。 - 处理
Authorization
头: 如果前端需要在请求头中传递Authorization
(JWT/Bearer Token),则必须:- 在
allowedHeaders
中包含
- 在