Spring Security - 跨域资源共享(CORS)详解
1. 什么是 CORS?
CORS(Cross-Origin Resource Sharing,跨域资源共享) 是一种 W3C 标准,用于解决浏览器中的跨域请求问题。通常情况下,浏览器安全策略限制了 Web 应用只能从同一个域名(origin)请求资源,而不能访问不同域名、端口或协议的资源。这是出于安全考虑,防止恶意网站未经授权读取用户数据。
然而,在实际的 Web 开发中,前端与后端分离架构越来越普遍,前端应用和后端 API 通常部署在不同的域名或端口上,这种场景下,跨域请求变得不可避免。CORS 允许服务器通过设置 HTTP 头来告诉浏览器,哪些跨域请求是被允许的。
2. CORS 基本原理
CORS 的工作原理依赖于 HTTP 请求中的一些特殊头字段。典型的跨域请求包括两种类型:
- 简单请求:只涉及 GET、HEAD、POST 方法,且不包含自定义的请求头。
- 预检请求(Preflight Request):如果请求方法不是简单方法(例如 PUT、DELETE),或包含自定义头字段,浏览器会先发送一个 OPTIONS 请求进行预检,询问服务器是否允许该跨域请求。
浏览器在处理跨域请求时,主要会涉及以下几个 HTTP 头字段:
Access-Control-Allow-Origin
:指定允许哪些源发起请求。Access-Control-Allow-Methods
:指定允许的 HTTP 方法,例如 GET、POST、PUT 等。Access-Control-Allow-Headers
:允许客户端发送的请求头。Access-Control-Allow-Credentials
:是否允许发送认证信息(如 Cookies)。Access-Control-Max-Age
:指定预检请求的结果在客户端缓存的时间。
3. Spring Security 与 CORS
Spring Security 的默认行为是拒绝所有跨域请求,因为它专注于安全保护。为了允许跨域请求,需要在 Spring Security 中配置 CORS 策略。
通常,CORS 配置分为两个部分:
- Spring MVC 层:通过
@CrossOrigin
注解或全局配置来设置允许的跨域规则。 - Spring Security 层:通过在
HttpSecurity
中启用 CORS 支持。
4. Spring MVC 层的 CORS 配置
在 Spring 框架中,@CrossOrigin
注解可以用于控制特定控制器或方法的跨域请求。下面是一个简单的示例:
@RestController
@RequestMapping("/api")
public class MyController {
@CrossOrigin(origins = "http://example.com") // 允许来自 http://example.com 的跨域请求
@GetMapping("/data")
public ResponseEntity<String> getData() {
return ResponseEntity.ok("Data from server");
}
}
在这个例子中,@CrossOrigin
注解指定了只允许 http://example.com
这个域的请求访问 /api/data
接口。如果需要允许多个域名,可以指定多个 URL:
@CrossOrigin(origins = {"http://example.com", "http://another.com"})
除了在控制器或方法上配置 CORS,还可以通过全局配置来统一管理 CORS 规则。这通常在 WebMvcConfigurer
中实现:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://example.com") // 允许的跨域源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 是否允许发送 Cookie
.maxAge(3600); // 预检请求的缓存时间
}
}
此配置全局应用于所有的 URL 模式,控制哪些跨域请求是被允许的。
5. 在 Spring Security 中启用 CORS 支持
当我们在 Spring MVC 中配置了 CORS,仍然需要在 Spring Security 中启用 CORS 支持,否则 Spring Security 的默认安全机制会拒绝这些跨域请求。
要在 Spring Security 中启用 CORS,只需要在 HttpSecurity
配置中调用 cors()
方法:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors() // 启用 CORS 支持
.and()
.csrf().disable() // 如果使用的是无状态的 REST API,通常会禁用 CSRF
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L); // 预检请求结果缓存 1 小时
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
在这个配置中,我们通过 cors()
方法启用了 Spring Security 对 CORS 请求的支持,并且通过 CorsConfigurationSource
自定义了允许的跨域规则。
5.1 Spring Security 中的 CORS 与 CSRF
在 RESTful API 开发中,通常会禁用 CSRF 保护,因为它主要用于防止跨站请求伪造攻击,这类攻击对无状态的 REST API 并没有太大影响。因此,在大多数情况下,开发者会同时禁用 CSRF 和启用 CORS 支持。
http
.cors()
.and()
.csrf().disable(); // 禁用 CSRF 防护
禁用 CSRF 不影响 CORS 的处理,反而简化了 REST API 的请求逻辑,尤其是对于跨域请求来说。
6. 处理 CORS 的常见问题
6.1 预检请求未通过
当浏览器发送预检请求(OPTIONS 请求)时,服务器需要正确响应带有 Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等头信息。如果没有正确配置,浏览器会阻止后续的跨域请求。
可以通过 Spring Security 的 CorsConfigurationSource
正确配置预检请求的响应:
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
确保服务器返回正确的 Access-Control-Allow-Methods
。
6.2 自定义头信息问题
当前端应用发送包含自定义头(例如 Authorization
)的跨域请求时,需要明确允许这些头信息:
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
如果服务器不允许这些自定义头信息,浏览器将不会继续请求。
6.3 Cookie 的跨域问题
如果需要在跨域请求中发送 Cookie,必须在 CORS 配置中允许 credentials
,同时前端的请求也需要设置 withCredentials
为 true
:
- 后端:
configuration.setAllowCredentials(true);
- 前端(例如使用 Axios):
axios.defaults.withCredentials = true;
此外,Access-Control-Allow-Origin
不能设置为 *
,而必须明确指定允许的域名。
7. 总结
Spring Security 对 CORS 的处理提供了非常灵活且强大的支持,允许开发者通过多种方式来配置跨域访问控制。典型的 CORS 处理包括在 Spring MVC 中进行全局或局部配置,并在 Spring Security 中启用对跨域请求的支持。
在实践中,确保前后端跨域请求的正常工作,通常需要结合正确的 CORS 规则设置、Cookie 管理以及请求头的处理。通过合理的配置,可以让跨域请求既安全又高效。