SpringSecurity实战教程(十二)CORS安全配置 - 跨域请求的安全详解
1. 前言
在日常开发中,跨域资源共享(CORS)已成为前端与后端分离架构的必备能力。正确配置 CORS 是后端安全边界的重要一环。但安全与便利的天平往往难以把握——过于宽松的配置可能导致敏感数据泄露,过于严格的策略又会影响正常业务交互。本文我们继续《Spring Security 实战教程》,讲解 CORS 安全配置。
2. 背景与原理概述
跨域请求是指浏览器在一个域(Origin A)加载的脚本访问另一个域(Origin B)的资源。为保护用户数据,浏览器默认禁止跨域访问,除非服务器在响应头中明确允许。CORS 机制通过 Access-Control-Allow-* 系列头部,告诉浏览器何种跨域请求被允许,从而实现安全的跨域通信。
2.1 预检请求全流程
当发起“简单请求”时(如 GET/POST 且只有简单头部),浏览器会直接带上 Cookie 等凭证;对复杂请求(如 PUT/DELETE、携带自定义头部)则先发起一次预检(OPTIONS)请求,确认服务器同意后才发送实际请求。

2.2 关键响应头说明
Access-Control-Allow-* 响应头主要包括以下几个:
| 响应头 | 安全意义 | 示例值 |
|---|---|---|
| Access-Control-Allow-Origin | 允许的源列表 | https://your-domain.com |
| Access-Control-Allow-Methods | 允许的HTTP方法 | GET,POST,PUT |
| Access-Control-Allow-Headers | 允许的请求头 | Content-Type,Authorization |
| Access-Control-Max-Age | 预检结果缓存时间(秒) | 86400 |
3. Spring MVC 中的 CORS 支持
Spring Framework 自 4.2 起在 MVC 层面引入了对 CORS 的原生支持,可通过全局配置或注解方式开启。
全局配置(WebMvcConfigurer)
@Configurationpublic class CorsGlobalConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") // 应用到所有 /api 路径 .allowedOrigins("https://your-domain.com") // 允许的源 .allowedMethods("GET", "POST", "PUT") // 允许的方法 .allowedHeaders("Content-Type", "Authorization") .exposedHeaders("X-Total-Count") .allowCredentials(true) // 允许携带 Cookie .maxAge(3600); // 预检结果缓存 1 小时 }}注解方式(@CrossOrigin)
@RestController@RequestMapping("/users")@CrossOrigin( origins = "https://your-domain.com", methods = {RequestMethod.GET, RequestMethod.POST}, allowCredentials = "true", maxAge = 1800)public class UserController { @GetMapping public List<User> list() { … }}4. Spring Security 安全配置方案
尽管 Spring MVC 已处理了 CORS,但如果启用了 Spring Security,必须在 Spring Security 配置中显式开启 CORS 支持,否则安全过滤器会在 MVC 之前拦截预检请求,导致跨域失败。
全局安全配置
@Configuration@EnableWebSecuritypublic class SecurityConfig {
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors .configurationSource(corsConfigurationSource()) ) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ); // 其他安全配置... return http.build(); }
@Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("https://your-domain.com")); config.setAllowedMethods(List.of("GET", "POST")); config.setAllowedHeaders(List.of("Authorization", "Content-Type")); config.setExposedHeaders(List.of("X-Custom-Header")); config.setMaxAge(3600L); config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }}5. 安全强化最佳实践
5.1 动态源配置方案
很多时候我们并不希望相关配置是写死在代码里的,可以构建 CorsConfigurationSource 来实现动态源配置方案,这里以读取环境变量举例:
@BeanCorsConfigurationSource corsConfigurationSource(Environment env) { CorsConfiguration config = new CorsConfiguration();
// 从环境变量读取允许的源 String allowedOrigins = env.getProperty("cors.allowed.origins", ""); config.setAllowedOrigins(Arrays.asList(allowedOrigins.split(",")));
config.applyPermitDefaultValues(); config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source;}5.2 安全头信息增强
@BeanSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(withDefaults()) .headers(headers -> headers .httpStrictTransportSecurity(hsts -> hsts .includeSubDomains(true) .preload(true) .maxAgeInSeconds(63072000) ) .xssProtection(xss -> xss .headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK) ) .contentSecurityPolicy(csp -> csp .policyDirectives("default-src 'self'") ) ); return http.build();}6. 前后端联合调试方案
假设前端部署在 https://frontend.app,后端 API 接口在 https://your-domain.com:
- 后端全局配置(见第4节 Spring Security 安全配置方案)允许该前端域名。
- 前端请求示例:
// 带凭证的跨域请求fetch('https://your-domain.com/data', { method: 'GET', credentials: 'include', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }}).then(response => { console.log('Allowed Headers:', response.headers.get('Access-Control-Allow-Headers'));});先发起
OPTIONS https://your-domain.com/data,带上Origin: https://frontend.app。
后端返回:
Access-Control-Allow-Origin: https://frontend.appAccess-Control-Allow-Credentials: trueAccess-Control-Allow-Methods: GET,POST,PUT,DELETEAccess-Control-Allow-Headers: Content-Type,Authorization7. 常见安全隐患与对策
7.1 风险矩阵
| 错误配置 | 潜在风险 | 解决方案 |
|---|---|---|
| Access-Control-Allow-Origin: * | 敏感数据泄露 | 动态白名单机制 |
| 允许全部HTTP方法 | 未授权操作风险 | 最小权限原则 |
| 暴露敏感响应头 | 信息泄露风险 | 严格控制 exposedHeaders |
| 缺失 Vary: Origin 头 | 缓存投毒攻击 | 自动添加 Vary 头 |
7.2 生产环境检查清单
- 禁用
allowedOrigins("*") - 设置合理的
maxAge值(建议 ≤ 1 小时) - 严格限制
allowedHeaders范围 - 启用
allowCredentials时必须指定具体源 - 结合 CSRF 保护关键操作
8. 结语
CORS 配置的本质是在便利性与安全性之间寻找平衡点。通过 Spring Security 的精细化配置,我们可以:
- 建立清晰边界:精确控制可交互的客户端范围
- 实施最小授权:按需开放必要的 HTTP 方法和头部
- 实现动态防御:根据环境变化自动调整安全策略
- 构建纵深防御:与 CSRF、CSP 等安全机制形成合力
# 生产环境推荐配置示例cors.allowed.origins=https://web-client.com,https://mobile-client.comcors.max-age=3600cors.exposed-headers=X-Request-ID通过本文内容,相信小伙伴们已经可以在 Spring Boot + Spring Security 项目中,按需灵活地设定跨域安全边界,既满足前后端分离的开发需求,又确保系统不被非授权域滥用。让您的应用将拥有更健壮的跨域防护能力。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
樊笼










