天天看點

cors 跨源資源共享

跨源資源共享 (​​CORS​​​) (或通俗地譯為跨域資源共享)是一種機制,該機制使用附加的 ​​HTTP​​ 頭來告訴浏覽器,準許運作在一個源上的Web應用通路位于另一不同源標明的資源。 當一個Web應用發起一個與自身所在源(域,協定和端口)不同的HTTP請求時,它發起的即跨源HTTP請求。

跨源HTTP請求的一個例子:運作在 http://domain-a.com 的JavaScript代碼使用​​XMLHttpRequest​​來發起一個到 https://domain-b.com/data.json 的請求。

出于安全性,浏覽器限制腳本内發起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源政策。 這意味着使用這些API的Web應用程式隻能從加載應用程式的同一個域請求HTTP資源,除非響應封包包含了正确CORS響應頭。

cors 跨源資源共享

跨源域資源共享( ​​CORS​​​ )機制允許 Web 應用伺服器進行跨源通路控制,進而使跨源資料傳輸得以安全進行。現代浏覽器支援在 API 容器中(例如 ​​XMLHttpRequest​​​ 或 ​​Fetch​​ )使用 CORS,以降低跨源 HTTP 請求所帶來的風險。

誰應該讀這篇文章?

說實話,每個人。

更具體地來講,這篇文章适用于網站管理者、後端和前端開發者。現代浏覽器處理跨源資源共享的用戶端部分,包括HTTP頭和相關政策的執行。但是這一新标準意味着伺服器需要處理新的請求頭和響應頭。對于服務端的支援,開發者可以閱讀補充材料 ​​cross-origin sharing from a server perspective (with PHP code snippets)​​ 。

什麼情況下需要 CORS ?

這份 ​​cross-origin sharing standard​​ 允許在下列場景中使用跨站點 HTTP 請求:

  • 前文提到的由 ​​XMLHttpRequest​​​ 或 ​​Fetch​​ 發起的跨源 HTTP 請求。
  • Web 字型 (CSS 中通過​

    ​ @font-face ​

    ​​使用跨源字型資源),​​是以,網站就可以釋出 TrueType 字型資源,并隻允許已授權網站進行跨站調用​​。
  • ​​WebGL 貼圖​​
  • 使用 ​

    ​drawImage​

    ​ 将 Images/video 畫面繪制到 canvas

本文概述了跨源資源共享機制及其所涉及的 HTTP 頭。

功能概述

跨源資源共享标準新增了一組 HTTP 首部字段,允許伺服器聲明哪些源站通過浏覽器有權限通路哪些資源。另外,規範要求,對那些可能對伺服器資料産生副作用的 HTTP 請求方法(特别是 ​​GET​​​ 以外的 HTTP 請求,或者搭配某些 MIME 類型的 ​​POST​​​ 請求),浏覽器必須首先使用 ​​OPTIONS​​​ 方法發起一個預檢請求(preflight request),進而獲知服務端是否允許該跨源請求。伺服器确認允許之後,才發起實際的 HTTP 請求。在預檢請求的傳回中,伺服器端也可以通知用戶端,是否需要攜帶身份憑證(包括 ​​Cookies ​​和 HTTP 認證相關資料)。

CORS請求失敗會産生錯誤,但是為了安全,在JavaScript代碼層面是無法獲知到底具體是哪裡出了問題。你隻能檢視浏覽器的控制台以得知具體是哪裡出現了錯誤。

接下來的内容将讨論相關場景,并剖析該機制所涉及的 HTTP 首部字段。

若幹通路控制場景

這裡,我們使用三個場景來解釋跨源資源共享機制的工作原理。這些例子都使用 ​​XMLHttpRequest​​ 對象。

本文中的 JavaScript 代碼片段都可以從 ​​http://arunranga.com/examples/access-control/​​​ 獲得。另外,使用支援跨源  ​​XMLHttpRequest​​ 的浏覽器通路該位址,可以看到代碼的實際運作結果。

關于服務端對跨源資源共享的支援的讨論,請參見這篇文章: ​​Server-Side_Access_Control (CORS)​​。

簡單請求

某些請求不會觸發 ​​CORS 預檢請求​​​。本文稱這樣的請求為“簡單請求”,請注意,該術語并不屬于 ​​Fetch​​ (其中定義了 CORS)規範。若請求滿足所有下述條件,則該請求可視為“簡單請求”:

  • 使用下列方法之一:
  • ​​GET​​
  • ​​HEAD​​
  • ​​POST​​
  • 除了被使用者代理自動設定的首部字段(例如 ​​Connection​​​ ,​​User-Agent​​​)和在 Fetch 規範中定義為 ​​禁用首部名稱​​​ 的其他首部,允許人為設定的字段為 Fetch 規範定義的 ​​對 CORS 安全的首部字段集合​​。該集合為:
  • ​​Accept​​
  • ​​Accept-Language​​
  • ​​Content-Language​​
  • ​​Content-Type​​ (需要注意額外的限制)
  • ​DPR​

  • ​Downlink​

  • ​Save-Data​

  • ​Viewport-Width​

  • ​Width​

  • ​​Content-Type​​ 的值僅限于下列三者之一:
  • ​text/plain​

  • ​multipart/form-data​

  • ​application/x-www-form-urlencoded​

  • 請求中的任意​​XMLHttpRequestUpload​​​ 對象均沒有注冊任何事件監聽器;​​XMLHttpRequestUpload​​​ 對象可以使用 ​​XMLHttpRequest.upload​​ 屬性通路。
  • 請求中沒有使用 ​​ReadableStream​​ 對象。

注意: 這些跨站點請求與浏覽器發出的其他跨站點請求并無二緻。如果伺服器未傳回正确的響應首部,則請求方不會收到任何資料。是以,那些不允許跨站點請求的網站無需為這一新的 HTTP 通路控制特性擔心。

注意: WebKit Nightly 和 Safari Technology Preview 為​​Accept​​​, ​​Accept-Language​​​, 和 ​​Content-Language​​​ 首部字段的值添加了額外的限制。如果這些首部字段的值是“非标準”的,WebKit/Safari 就不會将這些請求視為“簡單請求”。WebKit/Safari 并沒有在文檔中列出哪些值是“非标準”的,不過我們可以在這裡找到相關讨論:​​Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language​​​, ​​Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS​​​, and ​​Switch to a blacklist model for restricted Accept headers in simple CORS requests​​。其它浏覽器并不支援這些額外的限制,因為它們不屬于規範的一部分。

比如說,假如站點 http://foo.example 的網頁應用想要通路 http://bar.other 的資源。http://foo.example 的網頁中可能包含類似于下面的 JavaScript 代碼:

<span style="color:#333333"><code class="language-js"><span style="color:#0077aa">var</span> invocation <span style="color:#9a6e3a">=</span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">XMLHttpRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#0077aa">var</span> url <span style="color:#9a6e3a">=</span> <span style="color:#669900">'http://bar.other/resources/public-data/'</span><span style="color:#999999">;</span>
   
<span style="color:#0077aa">function</span> <span style="color:#dd4a68">callOtherDomain</span><span style="color:#999999">(</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
  <span style="color:#0077aa">if</span><span style="color:#999999">(</span>invocation<span style="color:#999999">)</span> <span style="color:#999999">{</span>    
    invocation<span style="color:#999999">.</span><span style="color:#dd4a68">open</span><span style="color:#999999">(</span><span style="color:#669900">'GET'</span><span style="color:#999999">,</span> url<span style="color:#999999">,</span> <span style="color:#990055">true</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
    invocation<span style="color:#999999">.</span>onreadystatechange <span style="color:#9a6e3a">=</span> handler<span style="color:#999999">;</span>
    invocation<span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span> 
  <span style="color:#999999">}</span>
<span style="color:#999999">}</span>
</code></span>      

用戶端和伺服器之間使用 CORS 首部字段來處理權限:

cors 跨源資源共享

分别檢視請求封包和響應封包:

<span style="color:#333333"><code class="language-shell">GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 <span style="color:#999999">(</span>Macintosh<span style="color:#999999">;</span> U<span style="color:#999999">;</span> Intel Mac OS X <span style="color:#990055">10.5</span><span style="color:#999999">;</span> en-US<span style="color:#999999">;</span> rv:1.9.1b3pre<span style="color:#999999">)</span> Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml<span style="color:#999999">;</span><span style="color:#ee9900">q</span><span style="color:#9a6e3a">=</span><span style="color:#990055">0.9</span>,*/*<span style="color:#999999">;</span><span style="color:#ee9900">q</span><span style="color:#9a6e3a">=</span><span style="color:#990055">0.8</span>
Accept-Language: en-us,en<span style="color:#999999">;</span><span style="color:#ee9900">q</span><span style="color:#9a6e3a">=</span><span style="color:#990055">0.5</span>
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8<span style="color:#999999">;</span><span style="color:#ee9900">q</span><span style="color:#9a6e3a">=</span><span style="color:#990055">0.7</span>,*<span style="color:#999999">;</span><span style="color:#ee9900">q</span><span style="color:#9a6e3a">=</span><span style="color:#990055">0.7</span>
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 <span style="color:#990055">200</span> OK
Date: Mon, 01 Dec <span style="color:#990055">2008</span> 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: <span style="color:#ee9900">timeout</span><span style="color:#9a6e3a">=</span><span style="color:#990055">2</span>, <span style="color:#ee9900">max</span><span style="color:#9a6e3a">=</span><span style="color:#990055">100</span>
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

<span style="color:#999999">[</span>XML Data<span style="color:#999999">]</span>
</code></span>      

第 1~10 行是請求首部。第10行 的請求首部字段 ​​Origin​​​ 表明該請求來源于 ​

​http://foo.example​

​。

第 13~22 行是來自于 http://bar.other 的服務端響應。響應中攜帶了響應首部字段 ​​Access-Control-Allow-Origin​​​(第 16 行)。使用 ​​Origin​​​ 和 ​​Access-Control-Allow-Origin​​​ 就能完成最簡單的通路控制。本例中,服務端傳回的 ​

​Access-Control-Allow-Origin: *​

​ 表明,該資源可以被任意外域通路。如果服務端僅允許來自 http://foo.example 的通路,該首部字段的内容如下:

​Access-Control-Allow-Origin: http://foo.example​

現在,除了 http://foo.example,其它外域均不能通路該資源(該政策由請求首部中的 ORIGIN 字段定義,見第10行)。​

​Access-Control-Allow-Origin​

​ 應當為 * 或者包含由 Origin 首部字段所指明的域名。

預檢請求

與前述簡單請求不同,“需預檢的請求”要求必須首先使用 ​​OPTIONS​​   方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料産生未預期的影響。

如下是一個需要執行預檢請求的 HTTP 請求:

<span style="color:#333333"><code class="language-js"><span style="color:#0077aa">var</span> invocation <span style="color:#9a6e3a">=</span> <span style="color:#0077aa">new</span> <span style="color:#dd4a68">XMLHttpRequest</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#0077aa">var</span> url <span style="color:#9a6e3a">=</span> <span style="color:#669900">'http://bar.other/resources/post-here/'</span><span style="color:#999999">;</span>
<span style="color:#0077aa">var</span> body <span style="color:#9a6e3a">=</span> <span style="color:#669900">'<?xml version="1.0"?><person><name>Arun</name></person>'</span><span style="color:#999999">;</span>
    
<span style="color:#0077aa">function</span> <span style="color:#dd4a68">callOtherDomain</span><span style="color:#999999">(</span><span style="color:#999999">)</span><span style="color:#999999">{</span>
  <span style="color:#0077aa">if</span><span style="color:#999999">(</span>invocation<span style="color:#999999">)</span>
    <span style="color:#999999">{</span>
      invocation<span style="color:#999999">.</span><span style="color:#dd4a68">open</span><span style="color:#999999">(</span><span style="color:#669900">'POST'</span><span style="color:#999999">,</span> url<span style="color:#999999">,</span> <span style="color:#990055">true</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
      invocation<span style="color:#999999">.</span><span style="color:#dd4a68">setRequestHeader</span><span style="color:#999999">(</span><span style="color:#669900">'X-PINGOTHER'</span><span style="color:#999999">,</span> <span style="color:#669900">'pingpong'</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
      invocation<span style="color:#999999">.</span><span style="color:#dd4a68">setRequestHeader</span><span style="color:#999999">(</span><span style="color:#669900">'Content-Type'</span><span style="color:#999999">,</span> <span style="color:#669900">'application/xml'</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
      invocation<span style="color:#999999">.</span>onreadystatechange <span style="color:#9a6e3a">=</span> handler<span style="color:#999999">;</span>
      invocation<span style="color:#999999">.</span><span style="color:#dd4a68">send</span><span style="color:#999999">(</span>body<span style="color:#999999">)</span><span style="color:#999999">;</span> 
    <span style="color:#999999">}</span>
<span style="color:#999999">}</span>

<span style="color:#9a6e3a">...</span><span style="color:#9a6e3a">...</span>
</code></span>      

上面的代碼使用 POST 請求發送一個 XML 文檔,該請求包含了一個自定義的請求首部字段(X-PINGOTHER: pingpong)。另外,該請求的 Content-Type 為 application/xml。是以,該請求需要首先發起“預檢請求”。

cors 跨源資源共享
<span style="color:#333333"><code class="language-html">OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


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://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain</code></span>      

預檢請求完成之後,發送實際請求:

<span style="color:#333333"><code>POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]</code></span>      

浏覽器檢測到,從 JavaScript 中發起的請求需要被預檢。從上面的封包中,我們看到,第 1~12 行發送了一個使用 ​

​OPTIONS 方法的“​

​​預檢請求​

​”。​

​ OPTIONS 是 HTTP/1.1 協定中定義的方法,用以從伺服器擷取更多資訊。該方法不會對伺服器資源産生影響。 預檢請求中同時攜帶了下面兩個首部字段:

<span style="color:#333333"><code>Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type</code></span>      

首部字段 ​​Access-Control-Request-Method​​​ 告知伺服器,實際請求将使用 POST 方法。首部字段 ​​Access-Control-Request-Headers​​​ 告知伺服器,實際請求将攜帶兩個自定義請求首部字段:​

​X-PINGOTHER​

​​ 與 ​

​Content-Type​

​。伺服器據此決定,該實際請求是否被允許。

第14~26 行為預檢請求的響應,表明伺服器将接受後續的實際請求。重點看第 17~20 行:

<span style="color:#333333"><code>Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400</code></span>      

首部字段​

​ Access-Control-Allow-Methods ​

​​表明伺服器允許用戶端使用​

​POST,​

​​ ​

​GET ​

​​和 ​

​OPTIONS​

​​ 方法發起請求。該字段與 ​​HTTP/1.1 Allow: response header​​ 類似,但僅限于在需要通路控制的場景中使用。

首部字段 ​

​Access-Control-Allow-Headers ​

​​表明伺服器允許請求中攜帶字段 ​

​X-PINGOTHER ​

​​與​

​ Content-Type​

​​。與​

​Access-Control-Allow-Methods ​

​​一樣,​

​Access-Control-Allow-Headers​

​ 的值為逗号分割的清單。

最後,首部字段 ​

​Access-Control-Max-Age​

​ 表明該響應的有效時間為 86400 秒,也就是 24 小時。在有效時間内,浏覽器無須為同一請求再次發起預檢請求。請注意,浏覽器自身維護了一個最大有效時間,如果該首部字段的值超過了最大有效時間,将不會生效。

預檢請求與重定向

大多數浏覽器不支援針對于預檢請求的重定向。如果一個預檢請求發生了重定向,浏覽器将報告錯誤:

The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight

Request requires preflight, which is disallowed to follow cross-origin redirect

CORS 最初要求該行為,不過​​在後續的修訂中廢棄了這一要求​​。

在浏覽器的實作跟上規範之前,有兩種方式規避上述報錯行為:

  • 在服務端去掉對預檢請求的重定向;
  • 将實際請求變成一個簡單請求。

如果上面兩種方式難以做到,我們仍有其他辦法:

  • 發出一個簡單請求(使用  ​​Response.url​​​ 或 ​​XHR.responseURL​​)以判斷真正的預檢請求會傳回什麼位址。
  • 發出另一個請求(真正的請求),使用在上一步通過​​Response.url​​​ 或 ​​XMLHttpRequest.responseURL​​獲得的URL。

不過,如果請求是由于存在 Authorization 字段而引發了預檢請求,則這一方法将無法使用。這種情況隻能由服務端進行更改。

附帶身份憑證的請求

​​XMLHttpRequest​​​ 或 ​​Fetch​​​ 與 CORS 的一個有趣的特性是,可以基于  ​​HTTP cookies​​​ 和 HTTP 認證資訊發送身份憑證。一般而言,對于跨源 ​​XMLHttpRequest​​​ 或 ​​Fetch​​ 請求,浏覽器不會發送身份憑證資訊。如果要發送憑證資訊,需要設定 ​

​XMLHttpRequest​

​的某個特殊标志位。

本例中,http://foo.example 的某腳本向 http://bar.other 發起一個GET 請求,并設定 Cookies:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}      

第 7 行将 ​

​XMLHttpRequest​

​​的 ​

​withCredentials​

​​ 标志設定為 ​

​true​

​​,進而向伺服器發送 Cookies。因為這是一個簡單 GET 請求,是以浏覽器不會對其發起“預檢請求”。但是,如果伺服器端的響應中未攜帶 ​

​Access-Control-Allow-Credentials: true​

​ ,浏覽器将不會把響應内容傳回給請求的發送者。

cors 跨源資源共享

用戶端與伺服器端互動示例如下:

<span style="color:#333333"><code>GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]</code></span>      

即使第 10 行指定了 Cookie 的相關資訊,但是,如果 bar.other 的響應中缺失 ​​Access-Control-Allow-Credentials​​​

​: true​

​(第 17 行),則響應内容不會傳回給請求的發起者。

附帶身份憑證的請求與通配符

對于附帶身份憑證的請求,伺服器不得設定 ​

​Access-Control-Allow-Origin​

​​ 的值為“​

​*​

​”。

這是因為請求的首部中攜帶了 ​

​Cookie​

​​ 資訊,如果 ​

​Access-Control-Allow-Origin​

​​ 的值為“​

​*​

​​”,請求将會失敗。而将 ​

​Access-Control-Allow-Origin​

​​ 的值設定為 ​

​http://foo.example​

​,則請求将成功執行。

另外,響應首部中也攜帶了 Set-Cookie 字段,嘗試對 Cookie 進行修改。如果操作失敗,将會抛出異常。

第三方 cookies

注意在 CORS 響應中設定的 cookies 适用一般性第三方 cookie 政策。在上面的例子中,頁面是在 `foo.example` 加載,但是第 20 行的 cookie 是被 `bar.other` 發送的,如果使用者設定其浏覽器拒絕所有第三方 cookies,那麼将不會被儲存。

HTTP 響應首部字段

本節列出了規範所定義的響應首部字段。上一小節中,我們已經看到了這些首部字段在實際場景中是如何工作的。

Access-Control-Allow-Origin

響應首部中可以攜帶一個 ​​Access-Control-Allow-Origin​​ 字段,其文法如下:

<span style="color:#333333"><code class="language-html">Access-Control-Allow-Origin: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>origin</span><span style="color:#999999">></span></span> | *
</code></span>      

其中,origin 參數的值指定了允許通路該資源的外域 URI。對于不需要攜帶身份憑證的請求,伺服器可以指定該字段的值為通配符,表示允許來自所有域的請求。

例如,下面的字段值将允許來自 http://mozilla.com 的請求:

<span style="color:#333333">Access-Control-Allow-Origin: http://mozilla.com</span>      

如果服務端指定了具體的域名而非“*”,那麼響應首部中的 Vary 字段的值必須包含 Origin。這将告訴用戶端:伺服器對不同的源站傳回不同的内容。

Access-Control-Expose-Headers

譯者注:在跨源通路時,XMLHttpRequest對象的getResponseHeader()方法隻能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要通路其他頭,則需要伺服器設定本響應頭。

​​Access-Control-Expose-Headers​​ 頭讓伺服器把允許浏覽器通路的頭放入白名單,例如:

<span style="color:#333333"><code class="language-html">Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
</code></span>      

這樣浏覽器就能夠通過getResponseHeader通路​

​X-My-Custom-Header​

​​和 ​

​X-Another-Custom-Header​

​ 響應頭了。

Access-Control-Max-Age

​​Access-Control-Max-Age​​ 頭指定了preflight請求的結果能夠被緩存多久,請參考本文在前面提到的preflight例子。

<span style="color:#333333"><code class="language-html">Access-Control-Max-Age: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>delta-seconds</span><span style="color:#999999">></span></span>
</code></span>      

​delta-seconds​

​ 參數表示preflight請求的結果在多少秒内有效。

Access-Control-Allow-Credentials

​​Access-Control-Allow-Credentials​​​ 頭指定了當浏覽器的​

​credentials​

​​設定為true時是否允許浏覽器讀取response的内容。當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用​

​credentials​

​。請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該字段,這個響應将被忽略掉,并且浏覽器也不會将相應内容傳回給網頁。

<span style="color:#333333"><code class="language-html">Access-Control-Allow-Credentials: true
</code></span>      

上文已經讨論了​​附帶身份憑證的請求​​。

Access-Control-Allow-Methods

​​Access-Control-Allow-Methods​​ 首部字段用于預檢請求的響應。其指明了實際請求所允許使用的 HTTP 方法。

<span style="color:#333333"><code class="language-html">Access-Control-Allow-Methods: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>method</span><span style="color:#999999">></span></span>[, <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>method</span><span style="color:#999999">></span></span>]*
</code></span>      

相關示例見​​這裡​​。

Access-Control-Allow-Headers

​​Access-Control-Allow-Headers​​ 首部字段用于預檢請求的響應。其指明了實際請求中允許攜帶的首部字段。

<span style="color:#333333"><code class="language-html">Access-Control-Allow-Headers: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>field-name</span><span style="color:#999999">></span></span>[, <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>field-name</span><span style="color:#999999">></span></span>]*
</code></span>      

HTTP 請求首部字段

本節列出了可用于發起跨源請求的首部字段。請注意,這些首部字段無須手動設定。 當開發者使用 XMLHttpRequest 對象發起跨源請求時,它們已經被設定就緒。

Origin

​​Origin​​ 首部字段表明預檢請求或實際請求的源站。

<span style="color:#333333"><code class="language-html">Origin: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>origin</span><span style="color:#999999">></span></span>
</code></span>      

origin 參數的值為源站 URI。它不包含任何路徑資訊,隻是伺服器名稱。

Note: 有時候将該字段的值設定為空字元串是有用的,例如,當源站是一個 data URL 時。

注意,在所有通路控制請求(Access control request)中,​​Origin​​ 首部字段總是被發送。

Access-Control-Request-Method

​​Access-Control-Request-Method​​ 首部字段用于預檢請求。其作用是,将實際請求所使用的 HTTP 方法告訴伺服器。

<span style="color:#333333"><code class="language-html">Access-Control-Request-Method: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>method</span><span style="color:#999999">></span></span>
</code></span>      

相關示例見​​這裡​​。

Access-Control-Request-Headers

​​Access-Control-Request-Headers​​ 首部字段用于預檢請求。其作用是,将實際請求所攜帶的首部字段告訴伺服器。

<span style="color:#333333"><code class="language-html">Access-Control-Request-Headers: <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>field-name</span><span style="color:#999999">></span></span>[, <span style="color:#990055"><span style="color:#990055"><span style="color:#999999"><</span>field-name</span><span style="color:#999999">></span></span>]*
</code></span>      

相關示例見​​這裡​​。

規範

Specification Status Comment

​​Fetch

CORS​​

Living Standard New definition; supplants CORS specification.
Unknown Unknown Initial definition.

浏覽器相容性

​​Update compatibility data on GitHub​​

Desktop Mobile
Chrome Edge Firefox Internet Explorer Opera Safari Android webview Chrome for Android Firefox for Android Opera for Android Safari on iOS Samsung Internet
​​Access-Control-Allow-Origin​​ Full support4 Full support12 Full support3.5 Full support10 Full support12 Full support4 Full support2 Full supportYes Full support4 Full support12 Full support3.2 Full supportYes

What are we missing?

Legend

  • IE 10 提供了對規範的完整支援,但在較早版本(8 和 9)中,CORS 機制是借由 XDomainRequest 對象完成的。
  • Firefox 3.5 引入了對 XMLHttpRequests 和 Web 字型的跨源支援(但最初的實作并不完整,這在後續版本中得到完善);Firefox 7 引入了對 WebGL 貼圖的跨源支援;Firefox 9 引入了對 drawImage 的跨源支援。

參見

  • ​​Code Samples Showing XMLHttpRequest​​
  • ​​Cross-Origin Resource Sharing From a Server-Side Perspective (PHP, etc.)​​
  • ​​Cross-Origin Resource Sharing specification​​
  • ​​XMLHttpRequest​​
  • ​​Fetch API​​
  • ​​Using CORS with All (Modern) Browsers​​
  • ​​Using CORS - HTML5 Rocks​​