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

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

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



                                                   「呃。。還行, 但不夠」
webpack


若是你常常跟AJAX call打交道,那麼你確定遇到過下面這個錯誤。web


若是你看到這條消息,意味着響應失敗了,但你仍是能在Console裏的Network標籤下,看到返回的數據。chrome

那麼,這裏究竟是怎麼一回事呢?express

跨源資源共享(CORS)

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

考慮到安全問題,在跨域標準化以前,若是你想調用一個節點在不一樣域的API, 是不存在的。這種調用,會由於 Same-Origin 政策被阻止。後端

設計CORS這種機制是爲了,第一,使你所發出的請求能表明你自身; 第二, 阻止那些流氓JS發出的請求; 第三,這種機制會被激活,不管什麼時候你發送請求到:api

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中所作的同樣。


                                        Adding a proxy in the middle

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

你能夠寫一個本身的平臺,或者使用一個已有的解決方案,好比

www.npmjs.com/package/cor…

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


更多關於 CORS

若是你想學更多關於CORS的細節,我推薦你去查看更加細節化的MDN article.

相關文章
相關標籤/搜索