一文說盡 CORS

CORS的英文全稱是"Cross-Origin Resource Sharing"。代表“跨域資源共享”,是一種用于在Web瀏覽器和Web服務(wù)器之間共享跨域資源的機(jī)制??缬蛸Y源是指來自不同域的網(wǎng)頁上的資源,如字體,圖像,腳本和樣式表等。CORS允許網(wǎng)站服務(wù)器提供一組規(guī)則,以確定哪些網(wǎng)站可以訪問其資源。
1.什么是跨域請求
跨域請求是指從一個Web頁面發(fā)出的請求,而且其目標(biāo)資源與當(dāng)前頁面不同源。
同源是指協(xié)議、域名和端口號都相同,而不僅僅是域名相同。
如果使用的協(xié)議、端口或域名有一個不同,它們就被視為不同源,默認(rèn)不可以進(jìn)行跨域訪問。
2.預(yù)檢(Preflight)請求
顧名思義,預(yù)檢請求用來檢查實際請求是否安全可行,確保接下來的實際請求不會引起安全問題。
瀏覽器會在發(fā)送預(yù)檢請求之后等待響應(yīng),只有當(dāng)服務(wù)器允許時,才會發(fā)送實際請求。
2.1 什么情況下會觸發(fā)預(yù)檢請求
預(yù)檢請求的反義詞是簡單請求,我們可以看下簡單請求的判斷標(biāo)準(zhǔn)
若請求滿足所有下述條件,則該請求是簡單請求:
請求方法使用下列方法之一:
GET
HEAD
POST
允許手動設(shè)置的標(biāo)頭字段為:
Accept
Accept-Language
Content-Language
Content-Type(需要注意額外的限制)
Range(只允許簡單的范圍標(biāo)頭值 如 bytes=256- 或 bytes=127-255)
Content-Type 標(biāo)頭的值僅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
XMLHttpRequest.upload 對象屬性上沒有注冊任何事件監(jiān)聽器
也就是說,沒有調(diào)用 xhr.upload.addEventListener(),以監(jiān)聽上傳請求。
請求中沒有使用 ReadableStream 對象。
如果不滿足上述任何一個條件,都會觸發(fā)預(yù)檢請求
2.2 預(yù)檢請求的CORS處理
在CORS中,預(yù)檢請求的目的是用于檢查跨域請求是否安全、可行。
預(yù)檢請求是在正式的跨域請求之前發(fā)起的一次探測,用于告知服務(wù)器,實際發(fā)送的跨域請求是否合法。
因此,對于預(yù)檢請求的處理,需要服務(wù)器端進(jìn)行相應(yīng)的配置。
預(yù)檢請求的處理步驟如下:
瀏覽器會先發(fā)送一個OPTIONS請求(預(yù)檢請求),包含了實際發(fā)送的跨域請求所需要的一些頭信息,比如請求方法、請求頭等。
服務(wù)器收到后,會根據(jù)請求頭中的信息進(jìn)行判斷,判斷跨域請求是否安全可行。
如果跨域請求合法,服務(wù)器會返回帶有特定CORS響應(yīng)頭的響應(yīng),告知瀏覽器實際發(fā)送的跨域請求是合法的。
瀏覽器接收到服務(wù)器的響應(yīng)后,會檢查響應(yīng)頭中的"CORS"信息。如果CORS響應(yīng)頭合法,則瀏覽器會發(fā)送實際的跨域請求,否則會拋出跨域錯誤,請求無法成功。
2.3 非預(yù)檢請求的CORS處理
對于非預(yù)檢請求,CORS會按照以下規(guī)則進(jìn)行處理:
如果響應(yīng)頭中沒有包含"Access-Control-Allow-Origin",或者它的值不包含當(dāng)前域名,則瀏覽器會拋出一個跨域錯誤,請求無法成功。
如果響應(yīng)頭中包含"Access-Control-Expose-Headers",則該響應(yīng)頭中列出的頭信息可以被前端JavaScript訪問。否則,這些頭信息將被瀏覽器攔截,無法被前端JavaScript訪問。
需要注意的是,雖然非預(yù)檢請求不需要發(fā)送OPTIONS請求,但仍然需要服務(wù)器返回合法的CORS響應(yīng)頭信息。
3.跨域有關(guān)的響應(yīng)頭
下面列出了和跨域有關(guān)的響應(yīng)頭:
Access-Control-Allow-Origin:哪些源可以訪問該資源。"*"表示所有的源都可以訪問該資源。
Access-Control-Allow-Credentials:是否允許發(fā)送Cookie。
Access-Control-Expose-Headers:哪些響應(yīng)頭可以被前端JavaScript訪問。默認(rèn)情況下,不允許訪問
Access-Control-Allow-Methods:哪些HTTP請求方法允許跨域訪問。默認(rèn)情況下,只允許GET、HEAD、POST方法。
Access-Control-Allow-Headers:允許的請求頭信息。默認(rèn)情況下,只允許常見的請求頭,Accept、Accept-Language、Content-Language、Content-Type。
Access-Control-Max-Age:預(yù)檢請求的有效期,單位為秒。有效期內(nèi),瀏覽器無需再次發(fā)送預(yù)檢請求。
4.跨域和cookie
如果需要在跨域請求中攜帶 Cookie,服務(wù)器需要設(shè)置響應(yīng)頭"Access-Control-Allow-Credentials"為true
同時前端需要設(shè)置請求頭"withCredentials"為true,否則無法攜帶 Cookie。
4.1 攜帶cookie時的限制
在跨域請求中攜帶Cookie時,瀏覽器會先發(fā)送OPTIONS預(yù)檢請求,以檢查是否允許跨域請求。
與不攜帶Cookie的情況相比,攜帶Cookie時"Access-Control-Allow-Origin"字段的限制更加嚴(yán)格:
字段不能為通配符*,否則瀏覽器會忽略"Access-Control-Allow-Credentials"字段的值,不會發(fā)送Cookie。
字段的值必須與請求頭中"Origin"字段的值完全相同。