在Spring Boot中支持CORS

背景知识

请参见Cross-Origin Resource Sharing (CORS),或其中文版HTTP访问控制(CORS)(译者提供了一些有用的注释)。

场景

将API服务器和前端网页部署在不同域上,使用jQuery-Ajax对接口进行访问时会出现跨域问题。

在实际场景中,发现FireFox进行简单请求(POST)时也会进行请求预检(CORS Pre-Flight)。

服务器应用需要同时保证对OPTIONS请求返回时头部带有允许跨域的相关字段且状态码为200而非403或其他。在实际场景中发现FireFox即使拿到了包含允许跨域字段的响应头部,但由于状态码非200,同样会认为服务器应用拒绝了跨域请求。

逻辑层面的允许跨域

在控制器类或方法头部添加@CrossOrigin注解,通过设置相关属性来进行配置。

1
2
3
4
5
6
@CrossOrigin
@RestController
@RequestMapping("/foo/bar")
public class FooBarController {
// ...
}

有关注解的介绍源码注释非常详尽,你可以在org.springframework.web.bind.annotation包中找到这个类。

同样也可以通过更加粗粒度的方式来实现全局的配置,所有的配置方式请参见CORS support in Spring Framework

安全层面的允许跨域

以上配置保证了我们的应用能够处理包括预检请求在内的相关跨域响应,现在只剩下了状态码的问题。

应用正确应答了预检请求并返回了跨域控制相关信息,但状态码依旧为403,此时考虑使用了Spring Security框架时对HTTP的过滤。

值得一提的是,如果使用CorsFilter的方式对跨域请求进行处理,需要加上cors()方法。在本例中,由于我们使用了注解进行自动配置,这里就不必在安全层面特意启用cors支持。

1
2
3
4
5
6
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
// ...
}

问题在于安全框架把预检请求作为了非法请求处理,这里我们允许所有带了跨域注解的路由。

1
2
3
4
// ...
.antMatchers(HttpMethod.OPTIONS, "/foo/**")
.permitAll()
// ...

浏览器端的跨域问题就得到了解决。

预检测试

在测试中,需要将OPTIONS请求发送至有控制器的路由,否则会直接404