定义

  • 跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求
  • 出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求(或者说服务器正常返回,但被浏览器拦截了)。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头

    使用场景

  • XMLHttpRequestFetch 发起的跨域 HTTP 请求。

  • Web 字体 (CSS 中通过@font-face使用跨域字体资源
  • WebGL 贴图
  • 使用 [drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage) 将 Images/video 画面绘制到 canvas

    功能

  • 跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源

  • 规范要求,对于非简单请求,浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。
  • 在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

    简单请求的定义

    若满足下文所有条件,则将该请求视为简单请求:
  1. 使用以下方法之一:
    1. GET
    2. HEAD
    3. POST
  2. 除了用户代理自动设置的首部字段(Connection、User-Agent)和在Fetch中被定义为 禁用首部名称 的头部(下文)之外,不存在除:
    • [Accept](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept)
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • [DPR](http://httpwg.org/http-extensions/client-hints.html#dpr)
    • [Downlink](http://httpwg.org/http-extensions/client-hints.html#downlink)
    • [Save-Data](http://httpwg.org/http-extensions/client-hints.html#save-data)
    • [Viewport-Width](http://httpwg.org/http-extensions/client-hints.html#viewport-width)
    • [Width](http://httpwg.org/http-extensions/client-hints.html#width)

之外的头部。

  1. Content-Type 的值为以下三者之一:
    1. text/plain
    2. multipart/form-data
    3. application/x-www-form-urlencoded
  2. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
  3. 请求中没有使用 ReadableStream 对象。

    Fetch中被定义为 禁用首部名称 的头部

    语雀内容

    预检请求(preflight)

  • 正如上文所说,非简单请求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
  • 预检请求中,通常带有两个字段:
    • Access-Control-Request-Method 告知服务器,要使用什么方式实际请求
    • Access-Control-Request-Headers 告知服务器,实际请求会携带的参数是什么
  • 预检请求的返回体中,通常带有三个字段:

    • Access-Control-Allow-Methods 表明服务器允许客户端以哪几种方式发起请求
    • Access-Control-Allow-Headers 表明服务器允许客户端携带哪几个头部
    • Access-Control-Max-Age 标明这次预检响应的有效时间,单位为秒,在有效时间内,浏览器无须为同一请求再次发起预检请求。

      携带身份凭证

  • 客户端可以通过设置 XMLHttpRequest 的 withCredentials 为true,从而向服务端发送cookie。这需要配合服务端响应的头部携带 Access-Control-Allow-Credentials: true , 不然浏览器将不会把响应内容返回给请求的发送者。

  • 对于附带身份凭证,比如请求头部带了cookie的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

CORS新增头部

响应

Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

  • origin 参数的值指定了允许访问该资源的外域 URI。
  • 对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
  • 如果服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。

    Access-Control-Expose-Headers

    Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

  • 跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。

  • 例如在示例中,浏览器就可以额外访问到 X-My-Custom-Header, X-Another-Custom-Header

    Access-Control-Max-Age

    Access-Control-Max-Age: <delta-seconds>

  • 指定了preflight请求的结果能够被缓存多久


Access-Control-Allow-Credentials

Access-Control-Allow-Credentials: true

  • 指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。当用在对 prefligh t预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials 。
  • 请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。

    Access-Control-Allow-Methods

    Access-Control-Allow-Methods: <method>[, <method>]*

  • 用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。


Access-Control-Allow-Headers

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

  • 用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

请求

Origin

Origin: <origin>

  • 表明预检请求或实际请求的源站。
  • 不包含任何路径信息,只是服务器名称。
  • 注意,在所有访问控制请求(Access control request)中,Origin 首部字段总是被发送。

    Access-Control-Request-Method

    Access-Control-Request-Method: <method>

  • 用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器。


Access-Control-Request-Headers

Access-Control-Request-Headers: <field-name>[, <field-name>]*

  • 用于预检请求。其作用是,将实际请求所携带的首部字段告诉服务器。