「JavaScript」JS四種跨域方式詳解

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

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

1. JSONP

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

如今你想要獲取其餘網站上的 JavaScript 腳本,你很是高興的使用 XMLHttpRequest 對象來獲取。可是瀏覽器一點兒也不配合你,無情的彈出了下面的錯誤信息:git

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 就派上用場了。github

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

因此 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.... <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.namestring 類型。

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

個人頁面(http://one.example.com/index.... <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/f...

兩種服務端相關跨域方法

「JavaScript」兩種服務端相關跨域方法詳解 ← 反向代理、CORS方法請點這裏

以爲不錯的話按頂部的推薦,讓更多人看到吧~ㄟ(▔▽▔ㄟ)

相關文章
相關標籤/搜索