CORS跨域问题与Spring Security配置
请详细解释**CORS(Cross-Origin Resource Sharing)**跨域资源共享机制。什么是跨域?浏览器同源策略是什么?简单请求和预检请求(Preflight)的区别?如何在Spring Boot / Spring Security中正确配置CORS(@CrossOrigin / WebMvcConfigurer / CorsFilter)?
回答
苦行僧
同源策略与跨域
同源:协议 + 域名 + 端口 三者完全相同。
跨域:不同源之间的请求被浏览器限制(默认只允许同源)。
简单请求 vs 预检请求
简单请求(无需预检):
- 方法:GET / HEAD / POST
- 头:Accept / Accept-Language / Content-Language / Content-Type(限text/plain、multipart/form-data、application/x-www-form-urlencoded)
预检请求(Preflight):
- 浏览器先发OPTIONS请求,携带:
Origin: https://a.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: Authorization
- 服务端响应:
Access-Control-Allow-Origin: https://a.comAccess-Control-Allow-Methods: PUT, POSTAccess-Control-Allow-Headers: AuthorizationAccess-Control-Max-Age: 3600
- 预检通过后,浏览器才发真实请求
Spring Boot CORS配置
1. @CrossOrigin(局部)
@RestController
@CrossOrigin(origins = "http://localhost:3000", allowedHeaders = "*")
public class UserController {
@GetMapping("/user")
@CrossOrigin(origins = "http://admin.example.com") // 可覆盖类级别
public User getUser() { ... }
}
2. WebMvcConfigurer(全局)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://frontend.com", "http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Authorization", "Content-Type")
.exposedHeaders("X-Total-Count") // 暴露给前端的响应头
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检缓存时间
}
}
3. Spring Security + CORS
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults()) // 启用CORS(使用CorsConfigurationSource)
.csrf(csrf -> csrf.disable()) // API通常禁用CSRF
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("*"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
}
注意事项:
allowCredentials(true)时,allowedOrigins不能为"*",需指定具体域名- 生产环境不要开放所有域名
- 代理服务器(Nginx)也可解决跨域