[譯] 理解CORS這一篇就夠了

YU魚發表於2018-02-02

credit:https://medium.com/@baphemot

譯自:https://medium.com/@baphemot/understanding-cors-18ad6b478e2b


[譯] 理解CORS這一篇就夠了

                                                   “呃。。還行, 但不夠”


如果你經常跟AJAX call打交道,那麼你肯定遇到過下面這個錯誤。

[譯] 理解CORS這一篇就夠了

如果你看到這條訊息,意味著響應失敗了,但你還是能在Console裡的Network標籤下,看到返回的資料。

那麼,這裡到底是怎麼一回事呢?

跨源資源共享(CORS)

你所遇到的這種行為就是瀏覽器跨域的實現。

考慮到安全問題,在跨域標準化之前,如果你想呼叫一個節點在不同域的API, 是不存在的。這種呼叫,會因為 Same-Origin 政策被阻止。

設計CORS這種機制是為了,第一,使你所發出的請求能代表你自身; 第二, 阻止那些流氓JS發出的請求; 第三,這種機制會被啟用,無論何時你傳送請求到:

1). 不同的域名。(eg. 應用在 example.com 呼叫 api.com)

2). 不同的子域名。(eg. 應用在 example.com 呼叫 api.com)

3). 不同的埠。(eg. 應用在 example.com 呼叫 example.com:3001)

4). 不同的協議。(eg. 應用在 example.com 呼叫 example.com)

通過這種機制,我們能夠阻止黑客的指令碼攻擊,以防當你登陸,比如銀行網站,的時候替換你的驗證資訊。

如果你的瀏覽器嘗試發起一個‘不簡單’的請求(比如: 一個請求包含了cookies, 或者 Content-type 不是application/x-ww-form-urlencoded, multipart/form-data 或者 text-plain )這時候會呼叫有一種叫做 預檢(preflight)的機制,然後會傳送一個options請求到伺服器。如果伺服器的響應,沒有攜帶特定的headers, 隨後的‘簡單‘getpost請求還是會傳送,但是瀏覽器不會允許JS去訪問的收到的資料。

如果你明確想要新增cookies,自定義headers和其他一些features,那這個請求就不再是一個‘簡單’請求。那麼伺服器就不會合理地響應,請求也不會被髮送。


Access-Control-Allow-What?

CORS使用很少的HTTP請求頭(在請求和響應裡都是),但是有一點你必須明白,而且有能力去在工作中應用:

Access-Control-Allow-Origin

這個請求頭一般會被伺服器端返回,它的值代表了哪些域名你有權可以訪問。 它的值可以為:

  • *允許訪問任何域
  • 一個安全驗證過的域名(eg. example.com)

如果你請求客戶端傳一些用作驗證的請求頭,比如cookies, 那麼你就不能將Access-Control-Allow-Origin的值設定為*—必須是安全驗證過的域名才可以!

Access-Control-Allow-Credentials

如果一個伺服器支援通過cookies來驗證,那麼必須要在響應裡帶上這個請求頭。

True是其唯一有有效的值。

Access-Control-Allow-Headers

一個逗號分隔的list,存放代表伺服器願意支援的請求頭。(eg. 比如x-authentication-token, 你需要將其包含在ACA header裡, 返回給前面提到的options請求, 否則你的請求會被blocked)

Access-Control-Expose-Headers

跟上面相似,這個請求頭包含一系列使用者可用的headers,這些headers會出現在真實響應裡,而且客戶端是可以使用的。其他的所有header會被blocked。

Access-Control-Allow-Methods

這個比較簡單,存放所有服務端支援的HTTP請求型別(比如getpost)。

Origin

客戶端請求頭的一部分,其值包含客戶端app啟動處的域名。 出於安全考慮,瀏覽器將不允許你去重寫這個值。


如何消除‘CORS’錯誤

你不得不承認CORS並不是一種‘錯誤’。它是一種預期的機制為了去保護使用者,你,還有你傳送請求的目標網站。

有時候缺乏合理的請求頭是客戶端的一種錯誤的行為(eg. 缺少驗證資訊比如API key)。

這裡我將給你一些方法去“解決錯誤”,選擇哪種方法,這取決於你所應用的場景:

A - 我開發前端,後端我認識,聽我的 ;)

嗯這當然是最好的情況, 你就可以去實現合理的CORS響應在你所請求的伺服器端。如果一個API正在使用node的express框架,你只要用一下cors的包就行了。如果你想使你的網站更加合理安全,你可能要考慮使用一個白名單給Access-Control-Allow-Origin請求頭。

B - 我開發前端,後端我不熟,暫時需要一個臨時的解決方案 :)

這是第二好的情況,因為這就是A情況,只不過有一些時間限制。如果你想臨時解決這個問題,你可以讓你的瀏覽器忽略CORS機制,舉個栗子,使用ACAO Chrome外掛,或者在用Chome的時候跑一下下面的flags:

chrome --disable-web-security --user-data-dir複製程式碼

重要:請記住這條命令會應用於所有網站,並且存在於整個瀏覽器會話中。請小心使用。

另一個路子就是,你可以使用devServer.proxy(假設你使用webpack去serve你的app)或者使用一個 CORS-as-a-service 解決方案,比如cors-anywhere.herokuapp.com/

C - 我開發前端,我想要調後端? 不存在的 :`(

好吧,現在事情就變得複雜了。首先,你應該可能需要搞清,為什麼伺服器端沒有傳送一個正確的請求頭。

可能它們不允許使用第三方的庫的app去訪問?可能它們的API只給伺服器端的應用使用, 而不是瀏覽器?可能你在請求時沒有傳送用於驗證的token?

如果你仍然認為你有能夠通過瀏覽器得到資料,你應該去寫一個自己的代理,存在於瀏覽器應用和API之間,就像我們在方案B中所做的一樣。

[譯] 理解CORS這一篇就夠了

                                        Adding a proxy in the middle

這個代理伺服器,不是必須和你的應用跑在相同的域上。只要使得這個代理伺服器,在與客戶端交流時支援CORS就可以。在與API交流時不是必須要支援CORS。

你可以寫一個自己的平臺,或者使用一個已有的解決方案,比如

www.npmjs.com/package/cor…

記住,這種方法可能存在安全風險,如果你想要支援驗證的話。


更多關於 CORS

如果你想學更多關於CORS的細節,我推薦你去檢視更加細節化的MDN article.


相關文章