跨源资源共享 (CORS)

安全地共享跨源资源

Mariko Kosaka

浏览器的同源策略会阻止从不同源读取资源。这种机制可以阻止恶意网站读取其他网站的数据,但也阻止了合法的用途。

现代 Web 应用通常希望从不同的源获取资源,例如,从不同的域检索 JSON 数据,或将另一个站点的图像加载到 <canvas> 元素中。这些可能是应该供任何人读取的公共资源,但同源策略会阻止它们的使用。开发者过去曾使用过诸如 JSONP 之类的变通方法。

跨源资源共享 (CORS) 以标准化的方式解决了这个问题。启用 CORS 后,服务器可以告知浏览器它可以使用额外的源。

Web 上的资源请求如何工作?

request and response
图示客户端请求和服务器响应。

浏览器和服务器可以使用超文本传输协议 (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' 添加到 fetch 选项,如下例所示。这会将 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 以外的方法的请求。
  • 包含 AcceptAccept-LanguageContent-Language 以外的标头的请求。
  • 具有 Content-Type 标头,但不是 application/x-www-form-urlencodedmultipart/form-datatext/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 标头,以秒为单位指定缓存预检结果的持续时间。这允许客户端发送多个复杂请求,而无需重复预检请求。