跨域资源共享 CORS 详解

本贴最后更新于 1009 天前,其中的信息可能已经斗转星移

要理解CORS需要先了解同源策略

同源策略

同源策略是一个重要的安全策略,它是浏览器最核心最基本的安全功能。

它限制web应用程序只能从加载应用程序的同一个域请求HTTP资源。

当向不同的域请求HTTP资源时就发生了跨域,默认请情况下浏览器会阻止跨域的请求。

那如何判断是否同源呢?

如果两个URL的协议,端口和主机都相同的话,则这两个URL是同源。

以下所有资源都具有相同的来源:

http://example.com/
http://example.com:80/
http://example.com/path/file

每个url都有相同的协议,主机和端口号

以下每个资源都与其他的不同源:

 http://example.com/
 http://example.com:8080/
 http://www.example.com/
 https://example.com:80/
 https://example.com/
 http://example.org/
 http://ietf.org/

所以所谓的同源策略简单的理解就是,打开某个页面后,这个页面上的ajax请求默认只能向和页面同源的url发送http请求。同源策略固然保证了安全,但同时也限制了应用的灵活性,所以出现了CORS.

什么是CORS

CORS是一个W3C标准,全称是"跨域资源共享"(cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

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

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS原理

跨域请求

浏览器将跨域请求分为两类:简单请求和非简单请求。

只要同时满足一下两个条件,就属于简单请求:

  1. 请求方法是一下三种方法之一:
    • head
    • get
    • post
  2. http请求的头信息不超出以下几种字段:
    • accept
    • accept-language
    • content-language
    • Last-Event-ID
    • Content-Type的值仅限于下列三者之一:
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded

简单请求CORS步骤

对于简单请求CORS的基本流程如下:

第一步:客户端(浏览器)请求

当浏览器发出跨域请求时,该浏览器会添加一个包含当前源(协议,主机和端口)的Origin头。

image.png

第二步:服务器响应

在服务器,当服务器看到Origin头并想要允许访问时,就需要在响应中加入一个Access-Control-Allow-Origin响应头来指定请求源(例如加入*表示允许任何源)

image.png

第三步:浏览器接受响应

当浏览器看到带有相应Access-Control-Allow-Origin响应头的响应时,即允许与客户端网站共享响应数据。否则抛出CORS异常。

注意同源策略只是浏览器遵守的规则,使用别的工具进行请求不会遵循同源策略的影响。

复杂请求CORS步骤

第一步:发送预检请求

浏览器会根据需要创建预检请求。该请求是一个options请求,会在实际请求消息之前被发送。

image.png

预检请求中关键请求头是origin表示请求来自哪个源。除了origin字段,预检请求头的信息还包含两个特殊字段。

  1. access-control-request-method

    该字段是必须的,用来列出接下来的CORS请求会用到哪些HTTP方法,上面图片中的是PATCH

  2. access-control-request-headers

    这个字段是一个逗号分隔的字符串,指定接下来的CORS请求还会携带哪些额外的字段,上面图片中的是content-type

第二步:响应预检请求

服务器收到预检请求后,检查origin,access-control-request-method,access-control-request-headers字段后,就可以返回响应。

image.png

响应中的access-control-allow-origin字段表示允许跨域的源,*表示允许任意跨域请求。其他CORS相关响应头如下:

  1. Access-Control-Allow-Methods

    逗号分隔的一个字符串,表明服务器允许的跨域请求方法

  2. Access-Control-Allow-Headers

    逗号分隔的一个字符串,表明服务器支持的头字段

  3. Access-Control-Max-Age

    该字段可选,用来指定本次预检请求的有效期,单位为秒。上图中的有效期是一天(86400秒),在此期间不用发出另一条预检请求。

注意:如果服务器否定了预检请求,也会返回一个正常的HTTP响应,但是不包含任何CORS相关的响应头。

第三步:发送跨域请求

一旦服务器通过了预检请求,以后每次浏览器正常的CORS请求,就跟简单请求一样,会有一个origin头字段。服务器的回应,也会有一个access-control-allow-origin头信息字段。

image.png

cookie跨域

出于隐私原因,CORS请求默认不带cookie。如果想要在使用CORS时发送cookie,就需要发送请求是携带cookie并且服务器也同意。

请求

ajax请求需要打开withCredentials属性才可以携带cookie:

const request = axios.create({
    baseURL: 'http://127.0.0.1:8000',
    timeout: 5000,
    withCredentials: true // 设置为true 跨域时会携带cookie
})
响应

如果要接受cookie跨域,access-control-allow-origin就不能设置为星号,必须指定明确,并且响应头中必须包含字段Access-Control-Allow-Credentials,值为true

同时cookie依然遵循同源策略,只有服务器指明的域名的cookie才会上传。

回帖
请输入回帖内容 ...