Access-Control-Allow-Origin跨域解決及詳細介紹

重要聲明:本文章僅僅代表了作者個人對此觀點的理解和表述。讀者請查閱時持自己的意見進行討論。

本文更新不及時,建議到原文地址瀏覽:跨域解決及詳細介紹

首先,跨域不是問題。是一種安全機制。 這是你在開發時、上線前就必須提前考慮到的安全問題並且採取合適的手段去避免這個問題帶來的程序錯誤。不過通常情況下,前端開發的小夥伴們都非常堅信後端小夥伴的接口一定已經處理好了跨域這個需求。然而事實上許多的前端拿到的都是沒有解決跨域的接口。又出於某種原因不便與後端交涉並且對方視乎態度不是很友好。在這種情況下作爲前端的小夥伴們心裏簡直一萬頭草泥馬飛過。

不過現在你不必爲之犯困了,哪個後端要是不協助處理跨域導致的一系列問題的話,請將本文直接甩給後臺,臉必須打響。要解決跨域必須由後端來一起協同解決,且主要解決工作在後端。

爲了能夠更加快速的解決跨域帶來的問題,下面對跨域進行詳細介紹。

一、跨域是什麼

跨域是瀏覽器加載了與當前域名、協議、端口不同另一站點下的資源,這與各大支持JavaScript的瀏覽器的同源策略是違背的。所謂同源策略,它是由Netscape提出的一個著名的安全策略。現在所有支持JavaScript 的瀏覽器都會使用這個策略。所謂同源是指,域名,協議,端口相同。

比如說,下面的幾個域名是同源的:

  1. http://example.com/
  2. http://example.com:80/
  3. http://example.com/path/file

它們都具有相同的協議、相同的域名、相同的端口(不指定端口默認80)。

而下面幾個域名是不同源的:

  1. http://example.com/
  2. http://example.com:8080/
  3. http://www.example.com/
  4. https://example.com:80/
  5. https://example.com/
  6. http://example.org/
  7. http://ietf.org/

它們有不同的協議或不同的域名或不同的端口,要注意頂級域名和二級域名也是認爲不同的域名。

二、解決跨域導致的問題

跨域並不會阻止請求的發出,也不會阻止請求的接受,跨域是瀏覽器爲了保護當前頁面,你的請求得到了響應,瀏覽器不會把響應的數據交給頁面上的回調,取而代之的是去提示你這是一個跨域數據。提示就是一個報錯提示,就像這樣:

跨域報錯截圖

我們知道了瀏覽器是如何處理的了,才能對症下藥來解決這個問題,下面介紹幾種常用的跨域解決方法:

1、CORS,跨域資源共享

這是最靠譜也是非常科學的解決方案,通過上面的截圖我們可以看到,它提示了一個:從某某位置請求的資源被阻擋了,因爲沒有在響應頭裏發現:"Access-Control-Allow-Origin"的響應頭。看到這個錯誤,我們不得不百度一下,這個Access-Control-Allow-Origin是個何方神聖。

通過Access-Control-Allow-Origin響應頭,就告訴了瀏覽器。如果請求我的資源的頁面是我這個響應頭裏記錄了的"源",則不要攔截此響應,允許數據通行。比如說下面示列了一個場景:

// 從 http://example.com 界面發出了一個請求到:http://example2.com,因爲不同源,導致了跨域。
// 而 http://example2.com 返回了下面的響應頭:
Content-Type: application/json;charset=utf-8
Content-Length: 3210
Server: apache
Access-Control-Allow-Origin: http://example.com

由於瀏覽器檢測到 http://example2.com 的響應頭中顯示的寫着:Access-Control-Allow-Origin: http://example.com,也就是,如果請求數據的源是 http://example.com 則可以允許訪問返回的數據。這樣瀏覽器就不會拋出錯誤提示,而是正確的將數據交給你的ajax回調。

在這個過程中跨域也存在,但跨域並沒有導致問題了。因爲後端的響應充分考慮到了某個頁面源要使用這個資源,早就幫對方做好了跨域資源共享。這纔可以順利的進行對接。

所以,要最簡單解決跨域導致的問題,只需要後端響應時,在響應頭裏指定允許調用資源的就可以了。除了設定指定的源以外,你還可以直接寫一個*號,這樣就表示:此數據允許被任何其他的進行獲取。

現在,你瞭解了Access-Control-Allow-Origin,其實除了它,還有與之相關的更多字段,它們也起到了更多的個性定值效果。下面進行了詳細介紹。

header頭字段 含義 取值
Access-Control-Allow-Credentials 響應頭表示是否可以將對請求的響應暴露給頁面。返回true則可以,其他值均不可以。 true/false
Access-Control-Allow-Headers 表示此次請求中可以使用那些header字段 符合請求頭規範的字符串
Access-Control-Allow-Methods 表示此次請求中可以使用那些請求方法 GET/POST(多個使用逗號隔開)

2、使用JSONP方案

當服務端沒有返回Access-Control-Allow-Origin這樣的字段時,是否就意味着不能使用此資源了嗎?不!只能說不建議使用此資源了。但我們還有另一種辦法,那就是通過JSONP。看到這個名字,似乎和json有關,說有也有,但也可以說沒有,JSONP只是大多數甚至全部人們對這種解決辦法的稱呼。

爲了更靈活的使用這中解決辦法,就必須要先了解它的實現原理。我們知道,在頁面內使用ajax加載別的域名下的數據時,是會被跨域阻止的。那有沒有辦法讓我們的請求不通過頁內的ajax,而是讓瀏覽器直接走這個請求?

有!如果你足夠細心,你會發現,<script>節點當有一個src值時,瀏覽器就會去加載這個js,然後並執行這個js文件,同樣的,<img>也可以設置一個src,瀏覽器會加載這個圖片並顯示。那麼,其中<script>節點在獲取到js後還會執行,而我們的業務邏輯代碼也是執行在相同的js環境下的。我們能不能想辦法,讓我們的請求不通過ajax,而是通過給body中追加一個<script>節點,這個節點的src值就是我們希望的要請求的目標接口,這樣,服務器端返回的數據不就繞過這個跨域限制,將數據拿回來了。

是的,不過千萬要注意,<script>要求你的返回內容必須是一段可以執行的js,因此你的返回數據就必須是一個可以執行的js語句,而不能是隨便一個字符串。並且還要保證在執行js後我們要知道數據回來了。那麼綜合這些考慮,我們想到了一個解決方案:

我們先定義一個方法:

// 注意這是前端代碼

var datasuccess = function (data) {
    // TODO
}

現在有了這個方法,我們將服務器返回的數據改成這種格式:

// 注意這是後端代碼

response.getWrite().print("datasuccess({name: \"Jack\", age: 23});");

後端通過返回一段js,而這段js實際上就是在執行之前定義好了的datasuccess方法,並且在執行的時候,還把一些數據傳入了進來。嘶~~,這是什麼啊,這不就正好我們可以在datasuccess方法裏面拿到返回的data數據嗎,而且還是在正確的時機進行執行。這樣,數據就名正言順的被我們拿到了啊!

它之所以叫JSONP,可能就是因爲幾乎所有後端在寫返回數據的時候都是將數據參數傳入的一個json對象。其實你可以甚至可以定義多個參數,每個參數的意義用途你也可以自己設定。

現在來看看一個完整的jsonp方法來進行跨域解決的代碼:

// 先定義要執行的方法。
var datasuccess =function(data) {
    console.log("數據已獲取:", data);
}

// 然後構建一個script節點,
var scriptDom = document.createElement("script");
scriptDom.src = "http://example2.com/?k=jack";
// 將節點添加到body,瀏覽器就會立即開始請求。當請求順利,就會執行 datasuccess 方法
// 在該方法裏執行獲取到請求數據的邏輯。

而通常我們的接搜數據的方法名稱並不是一直不變的,而是每次一個新的,在script節點中還會把方法名稱傳上去,讓服務端知道我們獲取數據的方法名,從而順利的完成調用。

儘管這個方法很好,但是它只能走GET的請求方法,因爲每次script節點的請求只有GET請求嘛。所以我們使用JSONP的接口,就只有GET方式。

三、VUE提供的代理配置

如果你是VUE項目,那麼你在開發時,通常會配置一個代理,來完成跨域問題的修復,似乎沒有後端的事情,但當你正式上線你才知道,代理沒效果了。是的。現在介紹一下這個代理幹了一件什麼事情。

當你在開發VUE項目時,就必然會開一個server去實時預覽你的代碼效果,這是毋庸置疑的。但你要注意,開了一個server,這個server能做到事情,可不就是單單給你提供預覽這麼簡單。它還可以進行請求轉發,實際上你配置的那些代理,是先會請求到你的server,你開的server檢查到你對應的配置,再請求你配的目標地址。這之間發生了什麼,實際上就是把你的實際請求轉到了你開的server裏面去請求了,這就不存在什麼瀏覽器同源安全的支配了,當然也就沒有了跨域問題。

而當你上線項目時,如果你的代理配置得不夠優雅,或者不夠標準,你要小心了,非常有可能你的請求就都會失敗。最佳的跨域解決方案,無非就是後端協助一起解決,單方面可不能達到完美。

四、總結

解決方式還有更多各種各樣的,但我認爲最優雅的莫過於這兩種,因此其他的解決方式可以暫時不提及,那些方法不僅增加了閱讀複雜度還增加了維護成本不建議使用。無論是採用何種方式,我們都是要讓後端修改代碼,修改header或修改返回數據格式,都離不開後端的參與,所以遇到跨域問題,趕快找後端,一起解決這個問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章