# 1.6 CORS

Spring MVC使您可以处理CORS（跨域资源共享）。本节介绍如何执行此操作。

## 1.6.1 介绍

出于安全原因，浏览器禁止AJAX调用当前来源以外的资源。例如，您可以将您的银行帐户放在一个标签中，将evil.com放在另一个标签中。来自evil.com的脚本不能使用您的凭据向您的银行API发出AJAX请求，例如从您的帐户中提取资金！

跨域资源共享（CORS）是大多数浏览器实施的W3C规范，可让您指定授权哪种类型的跨域请求，而不是使用基于IFRAME或JSONP的安全性较低且功能较弱的变通办法。

## 1.6.2 Processing

CORS规范区分飞行前，简单和实际请求。要了解CORS的工作原理，您可以阅读本文以及其他内容，或者参阅规范以获取更多详细信息。

Spring MVC HandlerMapping实现为CORS提供内置支持。成功将请求映射到处理程序后，HandlerMapping实现将检查给定请求和处理程序的CORS配置，并采取进一步的措施。飞行前请求直接处理，而简单和实际的CORS请求被拦截，验证并设置了必需的CORS响应标头。

为了启用跨域请求（即存在Origin标头，并且与请求的主机不同），您需要具有一些显式声明的CORS配置。如果找不到匹配的CORS配置，则预检请求将被拒绝。没有将CORS标头添加到简单和实际CORS请求的响应中，因此，浏览器拒绝了它们。

可以使用基于URL模式的CorsConfiguration映射分别配置每个HandlerMapping。在大多数情况下，应用程序使用MVC Java配置或XML名称空间声明此类映射，这导致将单个全局映射传递给所有HandlerMappping实例。

您可以将HandlerMapping级别的全局CORS配置与更细粒度的处理程序级别的CORS配置结合使用。例如，带注解的控制器可以使用类或方法级别的@CrossOrigin注解（其他处理程序可以实现CorsConfigurationSource）。

全局和本地配置组合的规则通常是相加的，例如，所有全局和所有本地起源。对于只能接受单个值的那些属性（例如allowCredentials和maxAge），局部属性将覆盖全局值。有关更多详细信息，请参见CorsConfiguration＃combine（CorsConfiguration）。

## 1.6.3 @CrossOrigin

@CrossOrigin批注启用带注解的控制器方法上的跨域请求，如以下示例所示：

```java
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}
```

默认情况下，@ CrossOrigin允许：

* All origins.
* All headers.
* 控制器方法映射到的所有HTTP方法。

默认情况下，allowedCredentials未启用，因为它建立了一个信任级别，可以公开敏感的用户特定信息（例如cookie和CSRF令牌），并且仅在适当的地方使用。

maxAge设置为30分钟。

@CrossOrigin在类级别上也受支持，并且被所有方法继承，如以下示例所示：

```java
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}
```

您可以在类级别和方法级别上使用@CrossOrigin，如以下示例所示：

```java
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin("https://domain2.com")
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}
```

## 1.6.4 Global Configuration

除了细粒度的控制器方法级别配置外，您可能还想定义一些全局CORS配置。 您可以在任何HandlerMapping上分别设置基于URL的CorsConfiguration映射。 但是，大多数应用程序使用MVC Java配置或MVC XNM命名空间来执行此操作。

默认情况下，全局配置启用以下功能：

* All origins.
* All headers.
* GET，HEAD和POST方法。

默认情况下，allowedCredentials未启用，因为它建立了一个信任级别，可以公开敏感的用户特定信息（例如cookie和CSRF令牌），并且仅在适当的地方使用。

maxAge设置为30分钟。

**Java Configuration**

要在MVC Java配置中启用CORS，可以使用CorsRegistry回调，如以下示例所示：

```java
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
            .allowedOrigins("https://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}
```

**XML Configuration**

要在XML名称空间中启用CORS，可以使用\<mvc：cors>元素，如以下示例所示：

```markup
<mvc:cors>

    <mvc:mapping path="/api/**"
        allowed-origins="https://domain1.com, https://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="true"
        max-age="123" />

    <mvc:mapping path="/resources/**"
        allowed-origins="https://domain1.com" />

</mvc:cors>
```

## 1.6.5 CORS Filter

您可以通过内置的CorsFilter应用CORS支持。

> 如果您尝试将CorsFilter与Spring Security一起使用，请记住Spring Security内置了对CORS的支持。

要配置过滤器，请将CorsConfigurationSource传递给其构造函数，如以下示例所示：

```java
CorsConfiguration config = new CorsConfiguration();

// Possibly...
// config.applyPermitDefaultValues()

config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);

CorsFilter filter = new CorsFilter(source);
```
