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。可通过指定注解属性自定义配置:originsmethodsallowedHeadersexposedHeadersallowCredentialsmaxAge

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)处理:

  1. 拦截CORS预检请求
  2. 拦截CORS简单请求和实际请求
  3. 添加相关CORS响应头(如Access-Control-Allow-Origin

CorsConfiguration指定CORS请求的处理方式,包括允许的来源、头部和方法等。可通过以下方式提供:

  • AbstractHandlerMapping#setCorsConfiguration():指定路径模式(如/api/**)到CorsConfiguration的映射
  • 子类重写AbstractHandlerMapping#getCorsConfiguration()方法
  • 处理器实现CorsConfigurationSource接口(如ResourceHttpRequestHandler

6. 总结

本文展示了Spring在应用中启用CORS的多种方式:

  1. 控制器级别:通过@CrossOrigin注解快速启用

    • 方法级别:精确控制单个接口
    • 类级别:统一配置整个控制器
    • 组合使用:灵活覆盖默认配置
  2. 全局配置:在配置类中统一管理

    • JavaConfig:推荐方式,类型安全
    • XML:传统项目适用
  3. Spring Security集成:确保安全框架不干扰CORS处理

✅ 最佳实践:

  • 开发环境可使用宽松配置(允许所有来源)
  • 生产环境严格限制允许的来源和方法
  • 结合Spring Security时优先使用CorsConfigurationSource bean

完整示例代码可在GitHub获取。


原始标题:CORS with Spring