1. 程式人生 > >跨域常見解決方案

跨域常見解決方案

div 請求 如果 主域 安全問題 highlight chrom call navig

由於考慮到安全性問題,HTML的同源策略不允許JavaScript進行跨域操作,但是隨著web端功能越來越多,對跨域需求逐漸增大,於是乎便催生了很多解決跨域的方法,通過網絡搜索和資料查詢,主要比較常見的解決方案有以下幾種:

一、設置 document.domain

  • 原理:相同主域名下不同子域頁面,通過設置document.domain讓他們同域;
  • 限制:此方法只適用與跨子域的,對於跨父域名,仍然不可行,且需要載入iframe頁面。
    // url http://bentos.com/foo
    var ifr = document.createElement(iframe);
    ifr.src 
    = http://b.bentos.com/foo; ifr.onload = function(){ var ifrdoc = ifr.contentDocument || ifr.contentWindow.document; ifrdoc.getElementsById("foo").innerHTML); }; ifr.style.display = none; document.body.appendChild(ifr);

    上述代碼所在的URL是http://bentos.com/foo,它對http://b.bentos.com/bar

    的DOM訪問要求後者將 document.domain往上設置一級

// URL http://b.bentos.com/bar
document.domain = bentos.com


// URL: http://b.bentos.com/foo
var data = {
    foo: bar,
    bar: foo
};
callback(data);

二、jsonp

  • 原理:<script>是可以跨域的,而且在跨域腳本中可以直接回調當前腳本的函數。
  • 限制:需要創建一個DOM對象並且添加到DOM樹,只能用於GET方法
// URL: http://b.bentos.com/foo
var data = { foo: bar, bar: foo }; callback(data);
// URL: http://bentos.com/foo
var callback = function(data){
    // 處理跨域請求得到的數據
                             };
var script = $(<script>, {src: http://b.bentos.com/bar});
$(body).append(script);

三、navigation 對象

  • 原理:iframe之間是共享navigator對象的,用它來傳遞信息
  • 要求:IE6/7

有些人註意到了IE6/7的一個漏洞:iframe之間的window.navigator對象是共享的。 我們可以把它作為一個Messenger,通過它來傳遞信息。比如一個簡單的委托:

// bentos.com
navigation.onData(){
    // 數據到達的處理函數
}
typeof navigation.getData === function 
    || navigation.getData()
// baidu.com
navigation.getData = function(){
    $.get(/path/under/baidu.com)
        .success(function(data){
            typeof navigation.onData === function
                || navigation.onData(data)
        });
}

document.navigator類似,window.name也是當前窗口所有頁面所共享的。也可以用它來傳遞信息。

四、window.postMessage

  • 原理:HTML5允許窗口之間發送消息
  • 限制:瀏覽器需要支持HTML5,獲取窗口句柄後才能相互通信

這是一個安全的跨域通信方法,postMessage(message,targetOrigin)也是HTML5引入的特性。 可以給任何一個window發送消息,不論是否同源。第二個參數可以是*但如果你設置了一個URL但不 相符,那麽該事件不會被分發。看一個普通的使用方式吧:

// URL: http://bentos.com/foo
var win = window.open(http://b.com/bar);
win.postMessage(Hello, bar!, http://b.com); 



// URL: http://baidu.com/bar
window.addEventListener(message,function(event) {
    console.log(event.data);
});

註意IE8及小於IE8的版本不支持addEventListener,需要使用attachEvent來監聽事件。參見:事件處理中的this:attachEvent, addEventListener, onclick

五、跨域資源共享(CORS)

  • 原理:服務器設置Access-Control-Allow-OriginHTTP響應頭之後,瀏覽器將會允許跨域請求
  • 限制:瀏覽器需要支持HTML5,可以支持POST,PUT等方法

例如,從http://a.com要訪問http://b.com的數據,通常情況下Chrome會因跨域請求而報錯:

1 XMLHttpRequest cannot load http://b.com. No ‘Access-Control-Allow-Origin‘ header is present on the requested resource. Origin ‘http://a.com‘ is therefore not allowed access.

錯誤原因是被請求資源沒有設置Access-Control-Allow-Origin,所以我們在b.com的服務器中設置這個響應頭字段即可:

1 Access-Control-Allow-Origin: *              # 允許所有域名訪問,或者
2 Access-Control-Allow-Origin: http://a.com   # 只允許所有域名訪問

在Html5出來之前,跨域采用常見方法為jsonp,jQuery也給出了支持。 值得註意的是它只是Hack,並沒有產生額外的安全問題。 因為JSONP要成功獲取數據,需要跨域資源所在服務器的配合,比如資源所在服務器需要自願地回調一個合適的函數,所以服務器仍然有能力控制資源的跨域訪問。

跨域的正道還是要使用HTML5提供的CORS頭字段以及window.postMessage, 可以支持POST, PUT等HTTP方法,從機制上解決跨域問題。 值得註意的是Access-Control-Allow-Origin頭字段是資源所在服務器設置的, 訪問控制的責任仍然是在提供資源的服務器一方,這和JSONP是一樣的道理。

跨域常見解決方案