博主在項目開發中,遇到一個問題,在當前網頁,發送一個跨域請求,可是瀏覽器給我報401:not authorizated。一開始以爲是我寫的http請求沒有帶入一些身份驗證信息,後來仔細研究了下,此跨域請求沒有帶包含認證信息的cookie,可是我看了項目中別的http請求壓根沒有單獨去設置這種cookie。
我又看了下Chrome—》 DevTools—》application—》Cookies,發現包含認證信息的cookie已經全部在當前域下了。說明我在發送請求時,並不需要自己在重新設置cookie值了;既然已經有cookie值,爲什麼我發送的請求的Request Headers裏沒有自動帶上已經存在的cookie呢?
所以我在懷疑是不是因爲我發送的請求時跨域的呢?
帶着這個懷疑,我着重看了下CORS跨域相關文檔。
一. CORS
Cross-Origin Resource Sharing 是 W3C 推出的一種跨站資源獲取的機制。
首先我們來看一下瀏覽器的支持情況:
Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
4 | 3.5 | 8 & 9(XDomainRequest), 10 | 12 | 4 |
移動端的瀏覽器對這種方法的支持比較完善。
現在我們看到了,如果不需要兼容 IE6、7的話,就可以使用這種方法。
這種跨域方案主要的思想是:服務器 在響應頭中設置相應的選項,瀏覽器如果支持這種方法的話就會將這種跨站資源請求視爲合法,進而獲取資源。
1.服務端可以設置的響應頭信息:
(1) Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
origin: 被允許跨域訪問這個資源的網站,* 代表全部網站。瀏覽器會檢測這個參數,如果符合要求,纔會去獲取資源。
舉個例子,允許 http://jasonkid.github.io/fezone 來跨域訪問這個資源:
Access-Control-Allow-Origin: http://jasonkid.github.io/fezone
(2) Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true | false
表示是否允許瀏覽器攜帶 Cookie 來訪問這個資源。
這個屬性要和 XMLHttpRequest 的 withCredentials 屬性來配合使用。var xhr = new XMLHttpRequest(); var url = 'http://foo.other/resources/credentialed-content/'; if(xhr) { xhr.open('GET', url, true); xhr.withCredentials = true; // 設置帶有 Cookie 的資源請求 xhr.onreadystatechange = handler; xhr.send(); }
2.能夠成功使用帶有 Cookie 的資源請求需要滿足以下幾個條件:
(1) XMLHttpRequest 對象中指定了 withCredentials = true
(2) 服務器響應頭中 Access-Control-Allow-Credentials: true
(3) 服務器響應頭中 Access-Control-Allow-Origin 不能爲 *
以下選項主要是安全性配置的問題,主要是服務器的配置問題了,就不展開介紹了:
Access-Control-Expose-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Headers
所以,在發送跨域請求時之所以沒能帶上cookie,很有可能是因爲ajax沒有設置`withCredentials = true`這一屬性。
那麼繼續研究下withCredentials屬性是幹啥吃的。。
二.withCredentials
查閱MSDN《火狐開發者文檔》,發現有詳細介紹:
XMLHttpRequest.withCredentials 屬性是一個Boolean類型,它指示了是否該使用類似cookies,authorization headers(頭部授權)或者TLS客戶端證書這一類資格證書來創建一個跨站點訪問控制(cross-site Access-Control)請求。在同一個站點下使用withCredentials屬性是無效的。
此外,這個指示也會被用做響應中cookies 被忽視的標示。默認值是false。
如果在發送來自其他域的XMLHttpRequest請求之前,未設置withCredentials 爲true,那麼就不能爲它自己的域設置cookie值。而通過設置withCredentials 爲true獲得的第三方cookies,將會依舊享受同源策略,因此不能被通過document.cookie或者從頭部相應請求的腳本等訪問。
注: 永遠不會影響到同源請求
Note: 不同域下的XmlHttpRequest 相應,不論其Access-Control- header 設置什麼值,都無法爲它自身站點設置cookie值,除非它在請求之前將withCredentials 設爲true。
實例
<code>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
</code>
詳見:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials
到此,已經很明白了,解決上述的問題就是需要設置xhr.withCredentials = true這一項。
但是,我是使用JQuery封裝的ajax,所以需要藉助$.ajax()方法的
xhrFields屬性。(xhrFields是一個具有多個”字段名稱-字段值”對的對象,用於對本地XHR對象進行設置。)
jQuery.ajax({ url: "https:" + _ENV_ + "api/1.0.0-beta/urlManager/getParameter", type:"get", contentType:"application/json", data:{ "md5":gs_all_params.gs_md5, "rt":new Date().getTime() }, xhrFields: { withCredentials: true }, success:function(response){ window[APP_ENV].gs_params =Object.assign(response.Parameter,gs_all_params) ; createApp(); }, error:function() { } });
運行後,不再報錯並且得到了正確的結果。
總結:
出現這個問題,是因爲本人對跨域請求帶上原網站的cookie的相關知識不太瞭解,通過這次報錯正好學習到了,因此記錄下來供大家借鑑。