1、神馬是跨域(Cross Domain)
說白點就是post、get的url不是你當前的網站,網域名稱不同。例如在aaa.com/a.html裡面,表單的提交action是bbb.com/b.html。
不僅如此,www.aaa.com和aaa.com之間也屬於跨域,因為www.aaa.com是次層網域,aaa.com是根網域名稱。
JavaScript出於安全方面的考慮,是不允許跨域調用其他頁面的對象的(同源策略 Same-Origin Policy)。
關於JavaScript能否跨域通訊的詳細說明,見下表:
http://www.a.com/a.js訪問以下URL的結果
| URL |
說明 |
是否允許通訊 |
| http://www.a.com/b.js |
同一網域名稱下 |
允許 |
| http://www.a.com/script/b.js |
同一網域名稱下不同檔案夾 |
允許 |
| http://www.a.com:8000/b.js |
同一網域名稱,不同連接埠 |
不允許 |
| https://www.a.com/b.js |
同一網域名稱,不同協議 |
不允許 |
| http://70.32.92.74/b.js |
網域名稱和網域名稱對應ip |
不允許 |
| http://script.a.com/b.js |
主域相同,子域不同 |
不允許 |
| http://a.com/b.js |
同一網域名稱,不同次層網域(同上) |
不允許 |
| http://www.b.com/b.js |
不同網域名稱 |
不允許 |
2、為嘛要跨域
跨域這東西其實很常見,例如我們可以把網站的一些指令碼、圖片或其他資源放到另外一個網站。例如我們可以使用Google提供的jQuery,載入時間少了,而且減少了伺服器的流量,如下
<script type="text/java script"src="https://aja x.googleapis.com/aj ax/libs/jquery/1.4.2/jquery.min.js"></script>
有時候不僅僅是一些指令碼、圖片這樣的資源,我們也會希望從另外的網站調用一些資料(有時候是不得不這樣),例如我希望擷取一些blog的RSS來產生一些內容,再或者說我在“人人開放平台”上開發一個應用,需要調用人人的資料。
然而,很不幸的是,直接用XMLHttpRequest來Get或者Post是不行的,例如我用jQuery的$.get去訪問本小博的主網域名稱 :
$.get("http://flycoder.org/",
{}, function(data){
alert('跨域不是越獄:'+data)
}, "html");
結果如下(總之就是不行啦~FF不報錯,但是木有返回資料):
那咋麼辦捏?(弱弱的說,測試的時候我發現IE訪問本地檔案時,是可以跨域的,不過這也沒啥用~囧~)
3、腫麼跨域
下面為了更好的講解和測試,我們可以通過修改hosts檔案來類比跨域的效果,hosts檔案在C:\Windows\System32\drivers\etc 檔案夾下。在下面加3行:
127.0.0.1 www.a.com
127.0.0.1 a.com
127.0.0.1 www.b.com
3.1、跨域代理
一種簡單的辦法,就是把跨域的工作交給伺服器,從後台擷取其他網站的資料再返回給前台,也就是跨域代理(Cross Domain Proxy)。
這種方法似乎蠻簡單的,改動也不太大。不過就是http請求多了些,響應慢了些,伺服器的負載重了些~
3.2、document.domain+iframe
對於主域相同而子域不同的例子,可以通過設定document.domain的辦法來解決。
舉www.a.com/a.html和a.com/b.html為例,只需在a.html中添加一個b.html的iframe,並且設定兩個頁面的document.domain都為'a.com'(只能為主網域名稱),兩個頁面之間即可互相訪問了,代碼如下:
www.a.com/a.html中的script
document.domain='a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
//擷取iframe的document對象
//W3C的標準方法是iframe.contentDocument,
//IE6、7可以使用document.frames[ID].document
//為了更好相容,可先擷取iframe的window對象iframe.contentWindow
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在這裡操縱b.html
alert(doc.getElementById("test").innerHTML);
};
a.com/b.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<script type="text/javascript">
document.domain='a.com';
</script>
</head>
<body>
<h1 id="test">Hello World</h1>
</body>
</html>
如果b.html要訪問a.html,可在子視窗(iframe)中通過window.parent來訪問父視窗的window對象,然後就可以為所欲為了(window對象都有了,還有啥不行的),同理子視窗也可以和子視窗之間通訊。
於是,我們可以通過b.html的XMLHttpRequest來擷取資料,再傳給a.html,從而解決跨子域擷取資料的問題。
但是這種方法只支援同一根網域名稱下的頁面,如果不同根網域名稱(例如baidu.com想訪問google.com)那就無能為力了。
3.3、動態script標籤(Dynamic Script Tag)
這種方法也叫“動態指令碼注入”。詳情
這種技術克服了XMLHttpRequest的最大限制,也就是跨域請求資料。直接用JavaScript建立一個新的指令碼標籤,然後設定它的src屬性為不同域的URL。
www.a.com/a.html中的script
var dynScript = document.createElement('script');
dynScript.src = 'http://www.b.com/b.js';
dynScript.setAttribute("type", "text/javascript");
document.getElementsByTagName('head')[0]
.appendChild(dynScript);
通過動態標籤注入的必須是可執行檔JavaScript代碼,因此無論是你的資料格式是啥(xml、json等),都必須封裝在一個回呼函數中。一個回呼函數如下:
www.a.com/a.html中的script
function dynCallback(data){
//處理資料, 此處簡單示意一下
alert(data.content);
}
在這個例子中,www.b.com/b.js需要將資料封裝在上面這個dynCallback函數中,如下:
1
dynCallback({content:'Hello World'})
我們看到了讓人開心的結果,Hello World~
不過動態指令碼注入還是存在不少問題的,下面我們拿它和XMLHttpRequest來對比一下:
| |
XmlHttpRequest |
Dynamic Script Tag |
| 跨瀏覽器安全色 |
No |
Yes |
| 跨域限制 |
Yes |
No |
| 接收HTTP狀態 |
Yes |
No (除了200) |
| 支援Get、Post |
Yes |
No (GET only) |
| 發送、接收HTTP頭 |
Yes |
No |
| 接收XML |
Yes |
Yes |
| 接收JSON |
Yes |
Yes |
| 支援同步、非同步 |
Yes |
No (只能非同步) |
可以看出,動態指令碼注入還是有不少限制,只能使用Get,不能像XHR一樣判斷Http狀態等。
而且使用動態指令碼注入的時候必須注意安全問題。因為JavaScript沒有任何許可權與存取控制的概念,通過動態指令碼注入的代碼可以完全控制整個頁面,所以引入外部來源的代碼必須多加小心
作者 jiangzhenghua