安全地共享跨源资源
浏览器的同源政策会阻止从不同来源读取资源。此机制会阻止恶意网站读取其他网站的数据,但也可以防止合法使用。
现代 Web 应用通常需要从不同来源获取资源,例如,从其他网域检索 JSON 数据,或将其他网站中的图片加载到 <canvas>
元素中。它们可以是公开资源,应可供任何人读取,但同源政策会阻止使用。开发者过去一直使用解决方案,例如 JSONP。
跨域资源共享 (CORS) 会以标准化方式解决此问题。 启用 CORS 可让服务器告知浏览器它可以使用其他来源。
资源请求是如何在网络上发挥作用的?
浏览器和服务器可以使用超文本传输协议 (HTTP) 通过网络交换数据。HTTP 定义了请求者和响应者之间的通信规则,包括获取资源所需的信息。
HTTP 标头用于协商客户端与服务器之间的消息交换,用于确定访问权限。浏览器的请求和服务器的响应消息分为标头和正文。
标题
有关消息的信息,例如消息类型或编码。标头可以包含以键值对表示的各种信息。请求标头和响应标头包含不同的信息。
请求标头示例
Accept: text/html
Cookie: Version=1
此标头等同于“我希望收到 HTML 作为响应。这是我有一个 Cookie。”
响应标头示例
Content-Encoding: gzip
Cache-Control: no-store
此标头等同于“此响应中的数据采用 gzip 编码。不要缓存此内容。”
正文
消息本身。它可以是纯文本、二进制图片、JSON、HTML 或许多其他格式。
CORS 的工作原理是什么?
同源政策会告知浏览器屏蔽跨源请求。当您需要来自其他来源的公开资源时,资源提供服务器会告知浏览器,发送请求的来源可以访问其资源。浏览器会记住这一点,并允许对该资源进行跨源资源共享。
第 1 步:客户端(浏览器)请求
当浏览器发出跨域请求时,浏览器会添加一个 Origin
标头,其中包含当前源站(架构、主机和端口)。
第 2 步:服务器响应
当服务器看到此标头并希望允许访问时,它会在响应中添加一个 Access-Control-Allow-Origin
标头来指定请求来源(或添加 *
以允许任何来源)。
第 3 步:浏览器收到响应
当浏览器看到此响应带有相应的 Access-Control-Allow-Origin
标头时,便会与客户端网站共享响应数据。
与 CORS 共享凭据
出于隐私保护方面的原因,CORS 通常用于无法识别请求者的匿名请求。如果您希望在使用 CORS 时发送 Cookie(能够识别发件人),则需要在请求和响应中添加其他标头。
请求
将 credentials: 'include'
添加到提取选项,如以下示例所示。该请求包含 cookie,如下所示:
fetch('https://example.com', {
mode: 'cors',
credentials: 'include'
})
响应
Access-Control-Allow-Origin
必须设置为特定来源(不使用 *
的通配符),必须将 Access-Control-Allow-Credentials
设置为 true
。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
复杂 HTTP 调用的预检请求
当 Web 应用发出复杂的 HTTP 请求时,浏览器会将预检请求添加到请求链的开头。
CORS 规范定义了一个复杂请求,如下所示:
- 使用 GET、POST 或 HEAD 以外方法的请求。
- 包含除
Accept
、Accept-Language
或Content-Language
以外的标头的请求。 - 具有
Content-Type
标头不是application/x-www-form-urlencoded
、multipart/form-data
或text/plain
的请求。
浏览器会自动创建任何必要的预检请求,并在实际请求消息之前发送这些请求。预检请求是一个 OPTIONS
请求,如以下示例所示:
OPTIONS /data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: DELETE
在服务器端,接收请求的应用会使用有关应用从以下来源接受的方法的信息来响应预检请求:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, DELETE, HEAD, OPTIONS
服务器响应还可以包含 Access-Control-Max-Age
标头,用于指定缓存预检结果的时长(以秒为单位)。这样,客户端就可以发送多个复杂请求,而无需重复预检请求。