1、概述
在任何现代浏览器中,随着 HTML5 和 JS 客户端的出现,跨域资源共享 (CORS)是一项相关规范,这些客户端通过 REST API 使用数据。
通常,服务于 JS 的主机(例如 example.com)与服务于数据的主机(例如 api.example.com)是不同的。在这种情况下,CORS 可以实现跨域通信。
Spring 为 CORS 提供一流的支持,为在任何 Spring 或 Spring Boot Web 应用程序中配置它提供了一种简单而强大的方式。
2.控制器方法CORS配置
启用 CORS 很简单——只需添加注释@CrossOrigin。
我们可以通过几种不同的方式实现这一点。
2.1。@CrossOrigin在@RequestMapping-带注释的处理程序方法上
@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注解设置任何配置,所以它使用默认值:
- 允许所有来源。
- 允许的 HTTP 方法是在@RequestMapping注释中指定的方法(本例中为 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()方法都启用了它。我们可以通过指定注释属性之一的值来自定义配置: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 配置。
在这里,两个方法的maxAge都是 3,600 秒,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 请求。
为了进一步锁定这一点,registry.addMapping方法返回一个CorsRegistration对象,我们可以将其用于其他配置。还有一个allowedOrigins方法可以让我们指定一个允许来源的数组。如果我们需要在运行时从外部源加载此数组,这将很有用。
此外,还有allowedMethods、allowedHeaders、exposedHeaders、maxAge 和allowCredentials,我们可以使用它们来设置响应标头和自定义选项。
3.2. XML 命名空间
这个最小的 XML 配置在/**路径模式上启用了 CORS,其默认属性与 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 MVC 之前拒绝该请求。
幸运的是,Spring Security 提供了一个开箱即用的解决方案:
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
这篇文章更详细地解释了它。
5. 它是如何工作的
CORS 请求会自动分派到各种已注册的HandlerMappings。它们处理 CORS 预检请求并使用CorsProcessor 实现(默认为DefaultCorsProcessor)拦截 CORS 简单和实际请求,以添加相关的 CORS 响应标头(例如Access-Control-Allow-Origin)。
CorsConfiguration允许我们指定应该如何处理 CORS 请求,包括允许的来源、标头和方法等。我们可以通过多种方式提供:
- AbstractHandlerMapping#setCorsConfiguration()允许我们指定一个Map,其中几个CorsConfiguration映射到路径模式,例如/api/**。
- 子类可以通过覆盖AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest)方法来提供自己的CorsConfiguration 。
- 处理程序可以实现CorsConfigurationSource 接口(就像现在的ResourceHttpRequestHandler一样)为每个请求提供CorsConfiguration。
6. 结论
在本文中,我们展示了 Spring 如何为在我们的应用程序中启用 CORS 提供支持。
我们从控制器的配置开始。我们看到我们只需要添加注解@CrossOrigin来启用 CORS 到一个特定的方法或整个控制器。
此外,我们了解到,为了在控制器之外控制 CORS 配置,我们可以使用 JavaConfig 或 XML 在配置文件中顺利执行此操作。
示例的完整源代码可在 GitHub 上获得。