1. 概述
在现代浏览器中,随着HTML5和通过REST API消费数据的JS客户端的兴起,跨域资源共享(CORS)已成为一项关键规范。
通常情况下,提供JS服务的宿主(如example.com)与提供数据的宿主(如api.example.com)不同。CORS正是在这种场景下实现跨域通信的解决方案。
Spring为CORS提供了一流的支持,在Spring或Spring Boot Web应用中配置CORS变得简单而强大。
2. 控制器方法级别的CORS配置
启用CORS非常简单——只需添加@CrossOrigin
注解即可。
我们可以通过几种不同方式实现:
2.1. 在@RequestMapping注解的处理方法上使用@CrossOrigin
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
✅ 上述示例仅对retrieve()
方法启用了CORS。由于未设置@CrossOrigin
的任何属性,将使用默认配置:
- 允许所有来源
- 允许
@RequestMapping
中指定的HTTP方法(本例中为GET) - 预检请求的缓存时间(maxAge)为30分钟
2.2. 在控制器类上使用@CrossOrigin
@CrossOrigin(origins = "http://example.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
⚠️ 这次在类级别添加了@CrossOrigin
,因此retrieve()
和remove()
方法都启用了CORS。可通过指定注解属性自定义配置:origins
、methods
、allowedHeaders
、exposedHeaders
、allowCredentials
或maxAge
。
2.3. 在控制器类和处理方法上同时使用@CrossOrigin
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("http://example.com")
@RequestMapping(method = RequestMethod.GET, "/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
Spring会合并两个注解的属性创建统一的CORS配置:
- 两个方法都将有3600秒的
maxAge
remove()
方法允许所有来源retrieve()
方法仅允许来自http://example.com
的请求
3. 全局CORS配置
作为细粒度注解配置的替代方案,Spring允许在控制器外部定义全局CORS配置。这类似于基于Filter的解决方案,但可在Spring MVC中声明,并与@CrossOrigin
细粒度配置结合使用。
默认情况下,允许所有来源以及GET、HEAD和POST方法。
3.1. Java配置方式
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
✅ 上述配置允许来自任何来源的CORS请求访问应用中的所有接口。
要进一步限制,可使用CorsRegistration
对象进行额外配置:
registry.addMapping("/api/**")
.allowedOrigins("https://trusted.com")
.allowedMethods("GET", "POST")
.maxAge(3600);
⚠️ 自Spring Boot 2.4.0起,新增了allowedOriginPatterns
提供更灵活的模式匹配。当allowCredentials
为true时,allowedOrigins
不能使用通配符*
,此时应改用allowedOriginPatterns
。
3.2. XML命名空间方式
最小化XML配置(与JavaConfig默认属性相同):
<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>
也可声明多个具有自定义属性的CORS映射:
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="http://domain1.com, http://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2"
allow-credentials="false"
max-age="123" />
<mvc:mapping path="/resources/**"
allowed-origins="http://domain1.com" />
</mvc:cors>
4. 结合Spring Security的CORS配置
如果项目中使用Spring Security,需要额外步骤确保CORS正常工作。因为CORS处理必须在Spring Security之前完成,否则请求会被Spring Security拦截。
Spring Security提供了开箱即用的解决方案:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
❌ 若未正确配置,Spring Security会拒绝CORS预检请求。
可通过添加CorsConfigurationSource
bean自定义CORS配置:
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://trusted.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowedHeaders(Arrays.asList("Authorization"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
5. 工作原理
CORS请求会自动分派到注册的HandlerMapping
。它们通过CorsProcessor
实现(默认为DefaultCorsProcessor
)处理:
- 拦截CORS预检请求
- 拦截CORS简单请求和实际请求
- 添加相关CORS响应头(如
Access-Control-Allow-Origin
)
CorsConfiguration
指定CORS请求的处理方式,包括允许的来源、头部和方法等。可通过以下方式提供:
AbstractHandlerMapping#setCorsConfiguration()
:指定路径模式(如/api/**
)到CorsConfiguration
的映射- 子类重写
AbstractHandlerMapping#getCorsConfiguration()
方法 - 处理器实现
CorsConfigurationSource
接口(如ResourceHttpRequestHandler
)
6. 总结
本文展示了Spring在应用中启用CORS的多种方式:
控制器级别:通过
@CrossOrigin
注解快速启用- 方法级别:精确控制单个接口
- 类级别:统一配置整个控制器
- 组合使用:灵活覆盖默认配置
全局配置:在配置类中统一管理
- JavaConfig:推荐方式,类型安全
- XML:传统项目适用
Spring Security集成:确保安全框架不干扰CORS处理
✅ 最佳实践:
- 开发环境可使用宽松配置(允许所有来源)
- 生产环境严格限制允许的来源和方法
- 结合Spring Security时优先使用
CorsConfigurationSource
bean
完整示例代码可在GitHub获取。