Spring Security安全配置:CORS跨域资源共享策略

Spring Security安全配置:CORS跨域资源共享策略

【免费下载链接】spring-security Spring Security 【免费下载链接】spring-security 项目地址: https://gitcode.com/gh_mirrors/spr/spring-security

1. 跨域资源共享(CORS)基础

跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种浏览器安全机制,用于控制不同源(协议、域名或端口不同)的网页如何相互访问资源。当前端应用(如React、Vue)与后端API部署在不同域名时,浏览器会实施同源策略限制跨域请求,此时需要通过CORS策略明确授权允许的跨域行为。

1.1 CORS核心概念

术语定义示例
源(Origin)协议+域名+端口的组合https://example.com:443
简单请求满足特定条件的GET/POST请求Content-Type: application/x-www-form-urlencoded
预检请求(Preflight)复杂请求前的OPTIONS探测请求带自定义头的PUT请求
跨域凭证是否允许跨域请求携带CookiewithCredentials: true

1.2 Spring Security中的CORS处理流程

mermaid

2. Spring Security CORS配置实现

Spring Security通过CorsConfigurer提供CORS配置能力,核心类位于org.springframework.security.config.annotation.web.configurers.CorsConfigurer,其工作原理是集成Spring Web的CorsFilter到安全过滤器链。

2.1 基础配置方式

2.1.1 Java配置方式
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 启用CORS配置
            .cors(cors -> cors
                // 配置CORS源
                .configurationSource(request -> {
                    CorsConfiguration config = new CorsConfiguration();
                    // 允许的源(生产环境建议指定具体域名)
                    config.setAllowedOrigins(Arrays.asList("https://frontend.example.com"));
                    // 允许的HTTP方法
                    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
                    // 允许的请求头
                    config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
                    // 暴露的响应头
                    config.setExposedHeaders(Arrays.asList("Custom-Header"));
                    // 预检请求缓存时间(秒)
                    config.setMaxAge(3600L);
                    // 是否允许携带凭证
                    config.setAllowCredentials(true);
                    // 应用到所有路径
                    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    source.registerCorsConfiguration("/**", config);
                    return source;
                })
            )
            // 其他安全配置
            .csrf(csrf -> csrf.disable()); // 生产环境需谨慎处理CSRF
        
        return http.build();
    }
}
2.1.2 基于Bean的配置方式
@Configuration
public class CorsConfig {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 允许所有源(开发环境测试用,生产环境禁止)
        configuration.setAllowedOriginPatterns(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
        configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 对/api/**路径应用CORS配置
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 使用CorsConfigurationSource bean
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf.disable());
            
        return http.build();
    }
}

2.2 关键配置参数详解

CorsConfiguration类提供了丰富的配置选项,以下是生产环境常用参数:

参数类型说明安全建议
allowedOriginsList 允许的源域名列表避免使用*,指定具体域名
allowedOriginPatternsList 允许的源域名模式Spring 5.3+支持,如https://*.example.com
allowedMethodsList 允许的HTTP方法遵循最小权限原则,只开放必要方法
allowedHeadersList 允许的请求头避免使用*,明确指定所需头
exposedHeadersList 允许暴露的响应头仅暴露前端需要访问的头
allowCredentialsboolean是否允许跨域凭证设为true时,allowedOrigins不能为*
maxAgeLong预检请求缓存时间(秒)建议设置3600以上减少预检请求次数

3. 高级场景配置

3.1 多路径差异化配置

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    
    // 公开API配置
    CorsConfiguration publicConfig = new CorsConfiguration();
    publicConfig.setAllowedOriginPatterns(Arrays.asList("*"));
    publicConfig.setAllowedMethods(Arrays.asList("GET"));
    publicConfig.setMaxAge(86400L);
    source.registerCorsConfiguration("/api/public/**", publicConfig);
    
    // 私有API配置
    CorsConfiguration privateConfig = new CorsConfiguration();
    privateConfig.setAllowedOrigins(Arrays.asList("https://admin.example.com"));
    privateConfig.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
    privateConfig.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
    privateConfig.setAllowCredentials(true);
    privateConfig.setMaxAge(3600L);
    source.registerCorsConfiguration("/api/private/**", privateConfig);
    
    return source;
}

3.2 与Spring MVC CORS的协同

当同时使用Spring MVC的@CrossOrigin注解和Spring Security CORS时,Security的CORS配置优先级更高。推荐统一通过Security配置全局CORS策略,避免配置冲突。

// Spring MVC控制器(局部CORS配置)
@RestController
@RequestMapping("/api")
public class ApiController {
    
    // 仅当Security未配置CORS时生效
    @CrossOrigin(origins = "https://legacy-frontend.example.com")
    @GetMapping("/legacy-data")
    public ResponseEntity<?> getLegacyData() {
        return ResponseEntity.ok("Legacy data");
    }
}

3.3 预检请求处理

Spring Security会自动处理预检请求(OPTIONS方法),无需额外配置。但需确保:

  1. 允许OPTIONS方法在allowedMethods
  2. 预检请求不被其他安全机制拦截
// 确保OPTIONS请求不被拦截
http
    .authorizeHttpRequests(auth -> auth
        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
        .anyRequest().authenticated()
    );

4. 常见问题与解决方案

4.1 跨域请求仍被拒绝

问题表现
Access to fetch at 'https://api.example.com/data' from origin 'https://frontend.example.com' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
排查步骤
  1. 确认CORS配置是否被正确应用
  2. 检查allowedOrigins是否包含请求源
  3. 验证CorsFilter是否被加入过滤器链
// 调试技巧:检查CORS配置是否生效
@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistration(CorsConfigurationSource configSource) {
    FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(new CorsFilter(configSource));
    registration.setName("corsFilter");
    registration.setOrder(Ordered.HIGHEST_PRECEDENCE); // 确保优先级最高
    return registration;
}

4.2 带凭证的跨域请求失败

问题根源

allowCredentials=true时,allowedOrigins不能设置为*,必须指定具体域名。

正确配置
// 错误配置
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowCredentials(true); // 此时会导致跨域失败

// 正确配置
config.setAllowedOrigins(Arrays.asList("https://frontend.example.com"));
config.setAllowCredentials(true);

4.3 预检请求403错误

解决方案

确保预检请求不被认证机制拦截:

http
    .authorizeHttpRequests(auth -> auth
        // 允许所有预检请求
        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
        // 其他请求需要认证
        .anyRequest().authenticated()
    );

5. 生产环境最佳实践

5.1 安全加固措施

安全措施实现方式
限制源域名使用allowedOriginPatterns替代allowedOrigins,如https://*.example.com
最小权限原则仅开放必要的HTTP方法和请求头
凭证保护启用allowCredentials时严格限制源域名
预检缓存设置合理的maxAge减少预检请求(建议86400秒)
HTTPS强制所有跨域通信使用HTTPS,避免中间人攻击

5.2 监控与日志

@Component
public class CorsLoggingFilter extends OncePerRequestFilter {
    
    private static final Logger logger = LoggerFactory.getLogger(CorsLoggingFilter.class);
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                   FilterChain filterChain) throws ServletException, IOException {
        
        // 记录跨域请求信息
        if (request.getHeader("Origin") != null) {
            logger.info("CORS Request - Origin: {}, Method: {}, Path: {}",
                request.getHeader("Origin"),
                request.getMethod(),
                request.getRequestURI());
        }
        
        filterChain.doFilter(request, response);
    }
}

// 注册过滤器
@Bean
public FilterRegistrationBean<CorsLoggingFilter> corsLoggingFilter() {
    FilterRegistrationBean<CorsLoggingFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(new CorsLoggingFilter());
    registration.addUrlPatterns("/*");
    registration.setName("corsLoggingFilter");
    registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return registration;
}

5.3 配置示例:生产环境CORS策略

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration config = new CorsConfiguration();
    
    // 生产环境指定具体域名
    config.setAllowedOriginPatterns(Arrays.asList(
        "https://app.example.com",
        "https://admin.example.com"
    ));
    
    // 仅允许必要HTTP方法
    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
    
    // 限制请求头
    config.setAllowedHeaders(Arrays.asList(
        "Authorization", 
        "Content-Type", 
        "Accept-Language"
    ));
    
    // 暴露必要响应头
    config.setExposedHeaders(Arrays.asList("X-Request-ID", "X-RateLimit-Remaining"));
    
    // 启用凭证支持
    config.setAllowCredentials(true);
    
    // 预检请求缓存24小时
    config.setMaxAge(86400L);
    
    // 应用到API路径
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    
    return source;
}

6. 总结与展望

CORS配置是Web应用安全的重要环节,Spring Security提供了灵活而强大的CORS支持机制。通过合理配置,可以在保障安全的同时提供良好的跨域体验。随着Web应用架构的演进,未来CORS配置可能会更加自动化,例如通过服务发现自动配置允许的源,但当前仍需开发人员明确配置安全策略。

在实施CORS时,应始终遵循最小权限原则,避免过度开放导致安全风险。同时,结合监控和日志系统,及时发现和解决跨域相关问题,确保应用在安全与可用性之间取得平衡。

【免费下载链接】spring-security Spring Security 【免费下载链接】spring-security 项目地址: https://gitcode.com/gh_mirrors/spr/spring-security

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值