文档首页> IDC服务> Spring Boot与Spring的跨域解决方案

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 预检请求。全局配置确保了预检请求能通过安全层。
    • 通常用于在全局配置基础上,对个别端点做特殊化处理。

🧪 验证与调试工具

  1. 浏览器开发者工具 (Network Tab): 检查请求/响应的 OriginAccess-Control-* 相关头信息,查看预检请求 (OPTIONS) 是否成功(状态码 200)及响应头是否正确。

  2. 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-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 等。


🔒 最佳实践与安全建议

  1. 优先选择方案一 (CorsFilter) 或方案二 (SecurityFilterChain 中配置): 确保全局生效且能正确处理预检请求。
  2. 严格限制 allowedOriginPatterns: 避免使用 "*",尤其是在 allowCredentials=true 时。明确列出信任的前端域名或使用可管理的模式 (如 https://*.yourcompany.com)。生产环境务必收紧!
  3. 按需开放方法和头信息: 避免 setAllowedMethods(List.of("*")) 和 setAllowedHeaders(List.of("*"))。只开放前端实际需要使用的 HTTP 方法和请求头。例如:List.of("GET", "POST", "Content-Type", "Authorization")
  4. 处理 Authorization 头: 如果前端需要在请求头中传递 Authorization (JWT/Bearer Token),则必须: