一、概述

跨域,即CORS,全称Cross-origin resource sharing, 跨域资源共享。是一个W3C的标准。

二、跨域所解决的问题

一般来说,浏览器具有同源策略,它限制了浏览器从一个源加载的文档或脚本,不能与来自另一个源的资源进行交互,这是一种保护策略。

同源的定义:两个源的 协议、域名、端口都相同,则他们是同源的。

同源策略控制了不同源之间的交互,这些交互通常分为如下三类:

  • 允许跨域写操作:如重定向和表单提交等
  • 允许跨域资源嵌入:如使用<img>嵌入图片,使用<script>嵌入脚本源文件等
  • 不允许跨域读操作:如使用js读取另一个源的信息

解决跨域问题的方式,官方提供了标准,就是CORS。

三、具体方案

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。

整个CORS通信过程,都是由浏览器自动完成,不需要我们去参与。当浏览器检测到跨域时,会自动添加一些消息用户不会有感觉。因此,CORS的关键是服务器

四、 两种请求

浏览器将CORS分为两类:简单请求和非简单请求

简单请求

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

满足以上条件的是简单请求,不满足的就是非简单请求

简单请求会自动在请求头上加一个Origin字段,说明了协议、域名、端口

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器根据该字段信息,决定是否同意该请求。如同意则返回消息头会包含如下信息

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

第一个是必须的:表示能够接受的域名请求

第二个是可选的:表示是否允许cookie发送

第三个是可选的:表示浏览器可以拿到的额外字段值。默认情况下CORS请求时只能拿到6个基本字段,这里指定了额外的字段。

非简单请求

非简单请求是对服务器有特殊要求的请求,如传输JSON等。

在进行传输前,会先进行一次查询请求,询问服务器是否支持跨域,以及支持哪些HTTP方法和字段。称为preflight。只有当查询通过后才会发送正式的消息。

一个preflight请求头如下,表示要发送PUT请求,并且额外发送的请求头字段为X-Custom-Header

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

响应如下,说明了允许的源,允许的方法,还有允许的请求头字段。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

上面通过后,浏览器将会正式发送跨域请求,就像简单请求的方式一样。

除了上面提到的几种返回字段外,还有一个字段

Access-Control-Max-Age: 1728000

它表示preflight的有效期,即本次preflight通过后,接下来的1728000秒内,该源不用再进行preflight操作即可直接发送请求了。

五、Spring对跨域的支持

局部跨域的支持

可以使用@CrossOrigin注解配合RequestMapping,针对某个特定的方法应用跨域设置,如下

@CrossOrigin(maxAge = 1800)
    @RequestMapping(value = "/check")
    @ResponseBody
    public APIResult<User> findBoxList(HttpServletRequest request) {

全局跨域支持

可以在配置文件中,使用mvc:cors配置全局的跨域支持

<mvc:cors>
    <mvc:mapping path="/**" allowed-origins="*" allow-credentials="true" max-age="1800" allowed-methods="GET,POST,DELETE,PUT,PATCH,OPTIONS"/>
</mvc:cors>
  • path: 指定适用的url
  • Allowed-origin: 允许的源
  • Allow-credentials: 允许发送cookie
  • Max-age: preflight的有效期
  • Allowed-methods: 允许的method
  • Allowed-headers: 允许的消息头字段
  • Exposed-headers: 额外允许的消息头字段

或者在实现了WebMvcConfigurer接口的配置类中重写方法

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域配置
     */
    @Override
    public void addCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
            .allowedOrigins("*").allowCredentials(true).maxAge(180)
            .allowedMethods("GET","POST","DELETE","PUT","PATCH","OPTIONS");
    }
	... ...
}

CORS过滤器

为了实现与其它不支持本地CORS的库一起使用,Spring还提供了一个更加通用的跨域过滤器,CorsFilter,继承该类,配置成过滤器即可工作。