跨源資源共享 (CORS) (或通俗地譯為跨域資源共享)是一種基于HTTP 頭的機(jī)制,該機(jī)制通過(guò)允許服務(wù)器標(biāo)示除了它自己以外的其它origin(域,協(xié)議和端口),這樣瀏覽器可以訪問(wèn)加載這些資源??缭促Y源共享還通過(guò)一種機(jī)制來(lái)檢查服務(wù)器是否會(huì)允許要發(fā)送的真實(shí)請(qǐng)求,該機(jī)制通過(guò)瀏覽器發(fā)起一個(gè)到服務(wù)器托管的跨源資源的"預(yù)檢"請(qǐng)求。在預(yù)檢中,瀏覽器發(fā)送的頭中標(biāo)示有HTTP方法和真實(shí)請(qǐng)求中會(huì)用到的頭。
跨源HTTP請(qǐng)求的一個(gè)例子:運(yùn)行在 http://domain-a.com 的JavaScript代碼使用XMLHttpRequest來(lái)發(fā)起一個(gè)到 https://domain-b.com/data.json 的請(qǐng)求。
出于安全性,瀏覽器限制腳本內(nèi)發(fā)起的跨源HTTP請(qǐng)求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味著使用這些API的Web應(yīng)用程序只能從加載應(yīng)用程序的同一個(gè)域請(qǐng)求HTTP資源,除非響應(yīng)報(bào)文包含了正確CORS響應(yīng)頭。

跨源域資源共享( CORS )機(jī)制允許 Web 應(yīng)用服務(wù)器進(jìn)行跨源訪問(wèn)控制,從而使跨源數(shù)據(jù)傳輸?shù)靡园踩M(jìn)行。現(xiàn)代瀏覽器支持在 API 容器中(例如
XMLHttpRequest 或 Fetch )使用 CORS,以降低跨源 HTTP 請(qǐng)求所帶來(lái)的風(fēng)險(xiǎn)。
誰(shuí)應(yīng)該讀這篇文章?
說(shuō)實(shí)話,每個(gè)人。
更具體地來(lái)講,這篇文章適用于網(wǎng)站管理員、后端和前端開(kāi)發(fā)者?,F(xiàn)代瀏覽器處理跨源資源共享的客戶端部分,包括HTTP頭和相關(guān)策略的執(zhí)行。但是這一新標(biāo)準(zhǔn)意味著服務(wù)器需要處理新的請(qǐng)求頭和響應(yīng)頭。對(duì)于服務(wù)端的支持,開(kāi)發(fā)者可以閱讀補(bǔ)充材料 cross-origin sharing from a server perspective (with PHP code snippets) 。
什么情況下需要 CORS ?
這份 cross-origin sharing standard 允許在下列場(chǎng)景中使用跨站點(diǎn) HTTP 請(qǐng)求:
- 前文提到的由
XMLHttpRequest或 Fetch 發(fā)起的跨源 HTTP 請(qǐng)求。 - Web 字體 (CSS 中通過(guò)
@font-face使用跨源字體資源),因此,網(wǎng)站就可以發(fā)布 TrueType 字體資源,并只允許已授權(quán)網(wǎng)站進(jìn)行跨站調(diào)用。 - WebGL 貼圖
- 使用
[drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)將 Images/video 畫(huà)面繪制到 canvas
本文概述了跨源資源共享機(jī)制及其所涉及的 HTTP 頭。
功能概述
跨源資源共享標(biāo)準(zhǔn)新增了一組 HTTP 首部字段,允許服務(wù)器聲明哪些源站通過(guò)瀏覽器有權(quán)限訪問(wèn)哪些資源。另外,規(guī)范要求,對(duì)那些可能對(duì)服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請(qǐng)求方法(特別是 GET 以外的 HTTP 請(qǐng)求,或者搭配某些 MIME 類型的 POST 請(qǐng)求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求(preflight request),從而獲知服務(wù)端是否允許該跨源請(qǐng)求。服務(wù)器確認(rèn)允許之后,才發(fā)起實(shí)際的 HTTP 請(qǐng)求。在預(yù)檢請(qǐng)求的返回中,服務(wù)器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認(rèn)證相關(guān)數(shù)據(jù))。
CORS請(qǐng)求失敗會(huì)產(chǎn)生錯(cuò)誤,但是為了安全,在JavaScript代碼層面是無(wú)法獲知到底具體是哪里出了問(wèn)題。你只能查看瀏覽器的控制臺(tái)以得知具體是哪里出現(xiàn)了錯(cuò)誤。
接下來(lái)的內(nèi)容將討論相關(guān)場(chǎng)景,并剖析該機(jī)制所涉及的 HTTP 首部字段。
若干訪問(wèn)控制場(chǎng)景
這里,我們使用三個(gè)場(chǎng)景來(lái)解釋跨源資源共享機(jī)制的工作原理。這些例子都使用 XMLHttpRequest 對(duì)象。
本文中的 JavaScript 代碼片段都可以從 http://arunranga.com/examples/access-control/ 獲得。另外,使用支持跨源 XMLHttpRequest 的瀏覽器訪問(wèn)該地址,可以看到代碼的實(shí)際運(yùn)行結(jié)果。
關(guān)于服務(wù)端對(duì)跨源資源共享的支持的討論,請(qǐng)參見(jiàn)這篇文章: Server-Side_Access_Control (CORS)。
簡(jiǎn)單請(qǐng)求
某些請(qǐng)求不會(huì)觸發(fā) CORS 預(yù)檢請(qǐng)求。本文稱這樣的請(qǐng)求為“簡(jiǎn)單請(qǐng)求”,請(qǐng)注意,該術(shù)語(yǔ)并不屬于 Fetch (其中定義了 CORS)規(guī)范。若請(qǐng)求滿足所有下述條件,則該請(qǐng)求可視為“簡(jiǎn)單請(qǐng)求”:
- 使用下列方法之一:
- 除了被用戶代理自動(dòng)設(shè)置的首部字段(例如
Connection,User-Agent)和在 Fetch 規(guī)范中定義為 禁用首部名稱 的其他首部,允許人為設(shè)置的字段為 Fetch 規(guī)范定義的 對(duì) CORS 安全的首部字段集合。該集合為:AcceptAccept-LanguageContent-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)
-
Content-Type的值僅限于下列三者之一:text/plainmultipart/form-dataapplication/x-www-form-urlencoded
- 請(qǐng)求中的任意
XMLHttpRequestUpload對(duì)象均沒(méi)有注冊(cè)任何事件監(jiān)聽(tīng)器;XMLHttpRequestUpload對(duì)象可以使用XMLHttpRequest.upload屬性訪問(wèn)。 - 請(qǐng)求中沒(méi)有使用
ReadableStream對(duì)象。
注意: 這些跨站點(diǎn)請(qǐng)求與瀏覽器發(fā)出的其他跨站點(diǎn)請(qǐng)求并無(wú)二致。如果服務(wù)器未返回正確的響應(yīng)首部,則請(qǐng)求方不會(huì)收到任何數(shù)據(jù)。因此,那些不允許跨站點(diǎn)請(qǐng)求的網(wǎng)站無(wú)需為這一新的 HTTP 訪問(wèn)控制特性擔(dān)心。
注意: WebKit Nightly 和 Safari Technology Preview 為Accept, Accept-Language, 和 Content-Language 首部字段的值添加了額外的限制。如果這些首部字段的值是“非標(biāo)準(zhǔn)”的,WebKit/Safari 就不會(huì)將這些請(qǐng)求視為“簡(jiǎn)單請(qǐng)求”。WebKit/Safari 并沒(méi)有在文檔中列出哪些值是“非標(biāo)準(zhǔn)”的,不過(guò)我們可以在這里找到相關(guān)討論: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。其它瀏覽器并不支持這些額外的限制,因?yàn)樗鼈儾粚儆谝?guī)范的一部分。
比如說(shuō),假如站點(diǎn) http://foo.example 的網(wǎng)頁(yè)應(yīng)用想要訪問(wèn) http://bar.other 的資源。http://foo.example 的網(wǎng)頁(yè)中可能包含類似于下面的 JavaScript 代碼:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
客戶端和服務(wù)器之間使用 CORS 首部字段來(lái)處理權(quán)限:
[圖片上傳失敗...(image-9d9e91-1608279432648)]
分別檢視請(qǐng)求報(bào)文和響應(yīng)報(bào)文:
GET /resources/public-data/ 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
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
第 1~10 行是請(qǐng)求首部。第10行 的請(qǐng)求首部字段 Origin 表明該請(qǐng)求來(lái)源于 http://foo.example。
第 13~22 行是來(lái)自于 http://bar.other 的服務(wù)端響應(yīng)。響應(yīng)中攜帶了響應(yīng)首部字段 Access-Control-Allow-Origin(第 16 行)。使用 Origin 和 Access-Control-Allow-Origin 就能完成最簡(jiǎn)單的訪問(wèn)控制。本例中,服務(wù)端返回的 Access-Control-Allow-Origin: * 表明,該資源可以被任意外域訪問(wèn)。如果服務(wù)端僅允許來(lái)自 http://foo.example 的訪問(wèn),該首部字段的內(nèi)容如下:
Access-Control-Allow-Origin: http://foo.example
現(xiàn)在,除了 http://foo.example,其它外域均不能訪問(wèn)該資源(該策略由請(qǐng)求首部中的 ORIGIN 字段定義,見(jiàn)第10行)。Access-Control-Allow-Origin 應(yīng)當(dāng)為 * 或者包含由 Origin 首部字段所指明的域名。
預(yù)檢請(qǐng)求
與前述簡(jiǎn)單請(qǐng)求不同,“需預(yù)檢的請(qǐng)求”要求必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)器,以獲知服務(wù)器是否允許該實(shí)際請(qǐng)求。"預(yù)檢請(qǐng)求“的使用,可以避免跨域請(qǐng)求對(duì)服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響。
如下是一個(gè)需要執(zhí)行預(yù)檢請(qǐng)求的 HTTP 請(qǐng)求:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
......
上面的代碼使用 POST 請(qǐng)求發(fā)送一個(gè) XML 文檔,該請(qǐng)求包含了一個(gè)自定義的請(qǐng)求首部字段(X-PINGOTHER: pingpong)。另外,該請(qǐng)求的 Content-Type 為 application/xml。因此,該請(qǐng)求需要首先發(fā)起“預(yù)檢請(qǐng)求”。
[圖片上傳失敗...(image-b68ab2-1608279432648)]
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">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</pre>
預(yù)檢請(qǐng)求完成之后,發(fā)送實(shí)際請(qǐng)求:
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]
瀏覽器檢測(cè)到,從 JavaScript 中發(fā)起的請(qǐng)求需要被預(yù)檢。從上面的報(bào)文中,我們看到,第 1~12 行發(fā)送了一個(gè)使用 OPTIONS 方法的“預(yù)檢請(qǐng)求”。 OPTIONS 是 HTTP/1.1 協(xié)議中定義的方法,用以從服務(wù)器獲取更多信息。該方法不會(huì)對(duì)服務(wù)器資源產(chǎn)生影響。 預(yù)檢請(qǐng)求中同時(shí)攜帶了下面兩個(gè)首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
首部字段 Access-Control-Request-Method 告知服務(wù)器,實(shí)際請(qǐng)求將使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服務(wù)器,實(shí)際請(qǐng)求將攜帶兩個(gè)自定義請(qǐng)求首部字段:X-PINGOTHER 與 Content-Type。服務(wù)器據(jù)此決定,該實(shí)際請(qǐng)求是否被允許。
第14~26 行為預(yù)檢請(qǐng)求的響應(yīng),表明服務(wù)器將接受后續(xù)的實(shí)際請(qǐng)求。重點(diǎn)看第 17~20 行:
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
首部字段Access-Control-Allow-Methods表明服務(wù)器允許客戶端使用POST, GET和 OPTIONS 方法發(fā)起請(qǐng)求。該字段與 HTTP/1.1 Allow: response header 類似,但僅限于在需要訪問(wèn)控制的場(chǎng)景中使用。
首部字段 Access-Control-Allow-Headers表明服務(wù)器允許請(qǐng)求中攜帶字段 X-PINGOTHER與Content-Type。與Access-Control-Allow-Methods一樣,Access-Control-Allow-Headers 的值為逗號(hào)分割的列表。
最后,首部字段 Access-Control-Max-Age 表明該響應(yīng)的有效時(shí)間為 86400 秒,也就是 24 小時(shí)。在有效時(shí)間內(nèi),瀏覽器無(wú)須為同一請(qǐng)求再次發(fā)起預(yù)檢請(qǐng)求。請(qǐng)注意,瀏覽器自身維護(hù)了一個(gè)最大有效時(shí)間,如果該首部字段的值超過(guò)了最大有效時(shí)間,將不會(huì)生效。
預(yù)檢請(qǐng)求與重定向
大多數(shù)瀏覽器不支持針對(duì)于預(yù)檢請(qǐng)求的重定向。如果一個(gè)預(yù)檢請(qǐng)求發(fā)生了重定向,瀏覽器將報(bào)告錯(cuò)誤:
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 最初要求該行為,不過(guò)在后續(xù)的修訂中廢棄了這一要求。
在瀏覽器的實(shí)現(xiàn)跟上規(guī)范之前,有兩種方式規(guī)避上述報(bào)錯(cuò)行為:
- 在服務(wù)端去掉對(duì)預(yù)檢請(qǐng)求的重定向;
- 將實(shí)際請(qǐng)求變成一個(gè)簡(jiǎn)單請(qǐng)求。
如果上面兩種方式難以做到,我們?nèi)杂衅渌k法:
- 發(fā)出一個(gè)簡(jiǎn)單請(qǐng)求(使用 Response.url 或 XHR.responseURL)以判斷真正的預(yù)檢請(qǐng)求會(huì)返回什么地址。
- 發(fā)出另一個(gè)請(qǐng)求(真正的請(qǐng)求),使用在上一步通過(guò)Response.url 或 XMLHttpRequest.responseURL獲得的URL。
不過(guò),如果請(qǐng)求是由于存在 Authorization 字段而引發(fā)了預(yù)檢請(qǐng)求,則這一方法將無(wú)法使用。這種情況只能由服務(wù)端進(jìn)行更改。
附帶身份憑證的請(qǐng)求
XMLHttpRequest 或 Fetch 與 CORS 的一個(gè)有趣的特性是,可以基于 HTTP cookies 和 HTTP 認(rèn)證信息發(fā)送身份憑證。一般而言,對(duì)于跨源 XMLHttpRequest 或 Fetch 請(qǐng)求,瀏覽器不會(huì)發(fā)送身份憑證信息。如果要發(fā)送憑證信息,需要設(shè)置 [XMLHttpRequest](https://developer.mozilla.org/en/DOM/XMLHttpRequest "En/XMLHttpRequest")的某個(gè)特殊標(biāo)志位。
本例中,http://foo.example 的某腳本向 http://bar.other 發(fā)起一個(gè)GET 請(qǐng)求,并設(shè)置 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](https://developer.mozilla.org/en/DOM/XMLHttpRequest "En/XMLHttpRequest")的 withCredentials 標(biāo)志設(shè)置為 true,從而向服務(wù)器發(fā)送 Cookies。因?yàn)檫@是一個(gè)簡(jiǎn)單 GET 請(qǐng)求,所以瀏覽器不會(huì)對(duì)其發(fā)起“預(yù)檢請(qǐng)求”。但是,如果服務(wù)器端的響應(yīng)中未攜帶 Access-Control-Allow-Credentials: true ,瀏覽器將不會(huì)把響應(yīng)內(nèi)容返回給請(qǐng)求的發(fā)送者。
[圖片上傳失敗...(image-90c967-1608279432647)]
客戶端與服務(wù)器端交互示例如下:
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]
即使第 10 行指定了 Cookie 的相關(guān)信息,但是,如果 bar.other 的響應(yīng)中缺失 Access-Control-Allow-Credentials: true(第 17 行),則響應(yīng)內(nèi)容不會(huì)返回給請(qǐng)求的發(fā)起者。
附帶身份憑證的請(qǐng)求與通配符
對(duì)于附帶身份憑證的請(qǐng)求,服務(wù)器不得設(shè)置 Access-Control-Allow-Origin 的值為“*”。
這是因?yàn)檎?qǐng)求的首部中攜帶了 Cookie 信息,如果 Access-Control-Allow-Origin 的值為“*”,請(qǐng)求將會(huì)失敗。而將 Access-Control-Allow-Origin 的值設(shè)置為 http://foo.example,則請(qǐng)求將成功執(zhí)行。
另外,響應(yīng)首部中也攜帶了 Set-Cookie 字段,嘗試對(duì) Cookie 進(jìn)行修改。如果操作失敗,將會(huì)拋出異常。
第三方 cookies
注意在 CORS 響應(yīng)中設(shè)置的 cookies 適用一般性第三方 cookie 策略。在上面的例子中,頁(yè)面是在 foo.example 加載,但是第 20 行的 cookie 是被 bar.other 發(fā)送的,如果用戶設(shè)置其瀏覽器拒絕所有第三方 cookies,那么將不會(huì)被保存。
HTTP 響應(yīng)首部字段
本節(jié)列出了規(guī)范所定義的響應(yīng)首部字段。上一小節(jié)中,我們已經(jīng)看到了這些首部字段在實(shí)際場(chǎng)景中是如何工作的。
Access-Control-Allow-Origin
響應(yīng)首部中可以攜帶一個(gè) Access-Control-Allow-Origin 字段,其語(yǔ)法如下:
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Allow-Origin: <origin> | *
</pre>
其中,origin 參數(shù)的值指定了允許訪問(wèn)該資源的外域 URI。對(duì)于不需要攜帶身份憑證的請(qǐng)求,服務(wù)器可以指定該字段的值為通配符,表示允許來(lái)自所有域的請(qǐng)求。
例如,下面的字段值將允許來(lái)自 http://mozilla.com 的請(qǐng)求:
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Allow-Origin: http://mozilla.com</pre>
如果服務(wù)端指定了具體的域名而非“*”,那么響應(yīng)首部中的 Vary 字段的值必須包含 Origin。這將告訴客戶端:服務(wù)器對(duì)不同的源站返回不同的內(nèi)容。
Access-Control-Expose-Headers
譯者注:在跨源訪問(wèn)時(shí),XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到一些最基本的響應(yīng)頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要訪問(wèn)其他頭,則需要服務(wù)器設(shè)置本響應(yīng)頭。
Access-Control-Expose-Headers 頭讓服務(wù)器把允許瀏覽器訪問(wèn)的頭放入白名單,例如:
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
</pre>
這樣瀏覽器就能夠通過(guò)getResponseHeader訪問(wèn)X-My-Custom-Header和 X-Another-Custom-Header 響應(yīng)頭了。
Access-Control-Max-Age
Access-Control-Max-Age 頭指定了preflight請(qǐng)求的結(jié)果能夠被緩存多久,請(qǐng)參考本文在前面提到的preflight例子。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Max-Age: <delta-seconds>
</pre>
delta-seconds 參數(shù)表示preflight請(qǐng)求的結(jié)果在多少秒內(nèi)有效。
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials 頭指定了當(dāng)瀏覽器的credentials設(shè)置為true時(shí)是否允許瀏覽器讀取response的內(nèi)容。當(dāng)用在對(duì)preflight預(yù)檢測(cè)請(qǐng)求的響應(yīng)中時(shí),它指定了實(shí)際的請(qǐng)求是否可以使用credentials。請(qǐng)注意:簡(jiǎn)單 GET 請(qǐng)求不會(huì)被預(yù)檢;如果對(duì)此類請(qǐng)求的響應(yīng)中不包含該字段,這個(gè)響應(yīng)將被忽略掉,并且瀏覽器也不會(huì)將相應(yīng)內(nèi)容返回給網(wǎng)頁(yè)。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Allow-Credentials: true
</pre>
上文已經(jīng)討論了附帶身份憑證的請(qǐng)求。
Access-Control-Allow-Methods
Access-Control-Allow-Methods 首部字段用于預(yù)檢請(qǐng)求的響應(yīng)。其指明了實(shí)際請(qǐng)求所允許使用的 HTTP 方法。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Allow-Methods: <method>[, <method>]*
</pre>
相關(guān)示例見(jiàn)這里。
Access-Control-Allow-Headers
[Access-Control-Allow-Headers](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Headers "響應(yīng)首部 Access-Control-Allow-Headers 用于 {{glossary("preflight request")}} (預(yù)檢請(qǐng)求)中,列出了將會(huì)在正式請(qǐng)求的 {{HTTPHeader("Access-Control-Request-Headers")}} 字段中出現(xiàn)的首部信息。") 首部字段用于預(yù)檢請(qǐng)求的響應(yīng)。其指明了實(shí)際請(qǐng)求中允許攜帶的首部字段。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Allow-Headers: <field-name>[, <field-name>]*
</pre>
HTTP 請(qǐng)求首部字段
本節(jié)列出了可用于發(fā)起跨源請(qǐng)求的首部字段。請(qǐng)注意,這些首部字段無(wú)須手動(dòng)設(shè)置。 當(dāng)開(kāi)發(fā)者使用 XMLHttpRequest 對(duì)象發(fā)起跨源請(qǐng)求時(shí),它們已經(jīng)被設(shè)置就緒。
Origin
Origin 首部字段表明預(yù)檢請(qǐng)求或?qū)嶋H請(qǐng)求的源站。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Origin: <origin>
</pre>
origin 參數(shù)的值為源站 URI。它不包含任何路徑信息,只是服務(wù)器名稱。
Note: 有時(shí)候?qū)⒃撟侄蔚闹翟O(shè)置為空字符串是有用的,例如,當(dāng)源站是一個(gè) data URL 時(shí)。
注意,在所有訪問(wèn)控制請(qǐng)求(Access control request)中,Origin 首部字段總是被發(fā)送。
Access-Control-Request-Method
Access-Control-Request-Method 首部字段用于預(yù)檢請(qǐng)求。其作用是,將實(shí)際請(qǐng)求所使用的 HTTP 方法告訴服務(wù)器。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Request-Method: <method>
</pre>
相關(guān)示例見(jiàn)這里。
Access-Control-Request-Headers
Access-Control-Request-Headers 首部字段用于預(yù)檢請(qǐng)求。其作用是,將實(shí)際請(qǐng)求所攜帶的首部字段告訴服務(wù)器。
<pre class="notranslate" style="box-sizing: border-box; border-left: 6px solid rgb(0, 69, 139); font-size: 1rem; line-height: 1.4; margin: 0px 0px 24px; max-width: 100%; overflow: auto; padding: 24px; width: 1002px; background-color: rgb(238, 238, 238);">Access-Control-Request-Headers: <field-name>[, <field-name>]*
</pre>
相關(guān)示例見(jiàn)這里。
規(guī)范
| Specification | Status | Comment |
| Fetch
<small lang="zh-CN" style="box-sizing: border-box;">CORS</small> | Living Standard | New definition; supplants CORS specification. |
| Unknown | Unknown | Initial definition. |
瀏覽器兼容性

此文章源于MND https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS