「JavaScript」四種跨域方式詳解


超詳細並且帶 Demo 的 JavaScript 跨域指南來了!

本文基於你了解 JavaScript 的同源策略,並且了解使用跨域跨域的理由。

1. JSONP

首先要介紹的跨域方法必然是 JSONP。

現在你想要獲取其他網站上的 JavaScript 腳本,你非常高興的使用 XMLHttpRequest 對象來獲取。但是瀏覽器一點兒也不配合你,無情的彈出了下面的錯誤信息:

XMLHttpRequest cannot load http://x.com/main.dat. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://y.com' is therefore not allowed access.

你心里肯定會想,我難道要用后台做個爬蟲來獲取這個數據嗎?!(;°○° )
為了避免這種蛋疼的事情發生,JSONP 就派上用場了。

<script> 標簽是不受同源策略的限制的,它可以載入任意地方的 JavaScript 文件,而並不要求同源。

所以 JSONP 的理念就是,我和服務端約定好一個函數名,當我請求文件的時候,服務端返回一段 JavaScript。這段 JavaScript 調用了我們約定好的函數,並且將數據當做參數傳入。
非常巧合的一點(其實並不是),JSON 的數據格式和 JavaScript 語言里對象的格式正好相同。所以在我們約定的函數里面可以直接使用這個對象。

光說不練假把式,讓我們來看一個例子:

你需要獲取數據的頁面 index.html:

<script> function getWeather(data) { console.log(data); } </script> <script src="http://x.y.com/xx.js">

http://x.y.com/xx.js 文件內容:

getWeather({
    "城市": "北京", "天氣": "大霧" });

我們可以看到,在我們定義了 getWeather(data) 這個函數后,直接載入了 xx.js。
在這個腳本中,執行了 getWeather 函數,並傳入了一個對象。然后我們在這個函數中將這個對象輸出到 console 中。

這就是整個 JSONP 的流程。

2. document.domain

使用條件:

  1. 有其他頁面 window 對象的引用。

  2. 二級域名相同。

  3. 協議相同。

  4. 端口相同。

document.domain 默認的值是整個域名,所以即使兩個域名的二級域名一樣,那么他們的 document.domain 也不一樣。

使用方法就是將符合上述條件頁面的 document.domain 設置為同樣的二級域名。這樣我們就可以使用其他頁面的 window 對象引用做我們想做的任何事情了。(╯▔▽▔)╯

補充知識:

  • x.one.example.com 和 y.one.example.com 可以將 document.domain 設置為 one.example.com,也可以設置為 example.com。

  • document.domain 只能設置為當前域名的一個后綴,並且包括二級域名或以上(.edu.cn 這種整個算頂級域名)。

我們直接操刀演示,用兩個網站 http://wenku.baidu.com/ 和 http://zhidao.baidu.com/
這兩個網站都是 http 協議,端口都是 80, 且二級域名都是 baidu.com。

打開 http://wenku.baidu.com/,在 console 中輸入代碼:

document.domain = 'baidu.com'; var otherWindow = window.open('http://zhidao.baidu.com/');

我們現在已經發現百度知道的網頁已經打開了,在百度知道網頁的 console 中輸入以下代碼:

document.domain = 'baidu.com';

現在回到百度文庫的網頁,我們就可以使用百度知道網頁的 window 對象來操作百度知道的網頁了。例如:

var divs = otherWindow.document.getElementsByTagName('div');

上面這個例子的使用方法並不常見,但是非常詳細的說明了這種方法的原理。
這種方法主要用在控制 <iframe> 的情況中。

比如我的頁面(http://one.example.com/index.html)中內嵌了一個 <iframe>

<iframe id="iframe" src="http://two.example.com/iframe.html"></iframe>

我們在 iframe.html 中使用 JavaScript 將 document.domain 設置好,也就是 example.com。

在 index.html 執行以下腳本:

var iframe = document.getElementById('iframe'); document.domain = 'example.com'; iframe.contentDocument; // 框架的 document 對象 iframe.contentWindow; // 框架的 window 對象

這樣,我們就可以獲得對框架的完全控制權了。

補充知識(絕對干貨):
當兩個頁面不做任何處理,但是使用了框架或者 window.open() 得到了某個頁面的 window 對象的引用,我們可以直接訪問的屬性有哪些?

方法
window.blur
window.close
window.focus
window.postMessage
window.location.replace
屬性 權限
window.closed 只讀
window.frames 只讀
window.length 只讀
window.location.href 只寫
window.opener 只讀
window.parent 只讀
window.self 只讀
window.top 只讀
window.window 只讀

3. window.name

我們來看以下一個場景:

隨意打開一個頁面,輸入以下代碼:

window.name = "My window's name"; location.href = "http://www.qq.com/";

再檢測 window.name :

window.name; // My window's name

可以看到,如果在一個標簽里面跳轉網頁的話,我們的 window.name 是不會改變的。
基於這個思想,我們可以在某個頁面設置好 window.name 的值,然后跳轉到另外一個頁面。在這個頁面中就可以獲取到我們剛剛設置的 window.name 了。

由於安全原因,瀏覽器始終會保持 window.name 是 string 類型。

這個方法也可以應用到與 <iframe> 的交互上來。

我的頁面(http://one.example.com/index.html)中內嵌了一個 <iframe>

<iframe id="iframe" src="http://omg.com/iframe.html"></iframe>

在 iframe.html 中設置好了 window.name 為我們要傳遞的字符串。
我們在 index.html 中寫了下面的代碼:

var iframe = document.getElementById('iframe'); var data = ''; iframe.onload = function() { data = iframe.contentWindow.name; };

定睛一看,為毛線報錯?
細心的讀者們肯定已經發現了,兩個頁面完全不同源啊!
由於 window.name 不隨着 URL 的跳轉而改變,所以我們使用一個暗黑技術來解決這個問題:

var iframe = document.getElementById('iframe'); var data = ''; iframe.onload = function() { iframe.onload = function(){ data = iframe.contentWindow.name; } iframe.src = 'about:blank'; };

或者將里面的 about:blank 替換成某個同源頁面(最好是空頁面,減少加載時間)。

補充知識:
about:blankjavascript: 和 data: 中的內容,繼承了載入他們的頁面的源。

這種方法與 document.domain 方法相比,放寬了域名后綴要相同的限制,可以從任意頁面獲取 string 類型的數據。

4. [HTML5] postMessage

在 HTML5 中, window 對象增加了一個非常有用的方法:

windowObj.postMessage(message, targetOrigin);
  • windowObj: 接受消息的 Window 對象。

  • message: 在最新的瀏覽器中可以是對象。

  • targetOrigin: 目標的源,* 表示任意。

這個方法非常強大,無視協議,端口,域名的不同。下面是烤熟的栗子:

var windowObj = window; // 可以是其他的 Window 對象的引用 var data = null; addEventListener('message', function(e){ if(e.origin == 'http://jasonkid.github.io/fezone') { data = e.data; e.source.postMessage('Got it!', '*'); } });

message 事件就是用來接收 postMessage 發送過來的請求的。函數參數的屬性有以下幾個:

  • origin: 發送消息的 window 的源。

  • data: 數據。

  • source: 發送消息的 Window 對象。

Demo

https://github.com/JasonKid/fezone/tree/master/JavaScript/%E5%87%A0%E7%A7%8D%E8%B7%A8%E5%9F%9F%E6%96%B9%E6%A1%88%E8%AF%A6%E8%A7%A3

兩種服務端相關跨域方法

「JavaScript」兩種服務端相關跨域方法詳解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM