什么是跨域?列出幾種JS跨域解決方法?(前端面試題)


什么是跨域?

現代瀏覽器出於安全考慮,都會去遵守一個叫做“同源策略”(同源策略就是用來限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互)的約定,同源的意思是兩個地址的協議、域名、端口號都相同的情況下,才叫同源。這個時候兩個地址才可以相互訪問 cookie、localStorage、sessionStorage、發送 ajax 請求,如果三者有一個不同,就是不同源,這時再去訪問這些資源就叫做跨域。
備注:
 1、端口和協議的不同,只能通過后台來解決
 2、localhost和127.0.0.1雖然都指向本機,但也屬於跨域;

但請求不會攜帶 cookie時,也就沒有跨域限制,如下:

  • js、css、image 等靜態文件
  • form 表單提交

同源策略限制了一下行為:

Cookie、LocalStorage 和 IndexDB 無法讀取

DOM 和 JS 對象無法獲取

Ajax請求發送不出去

如何解決跨域問題?

1.JSONP方法;

2.window.name方法;

3.document.domain方法;

4.window.postMessage方法;

 

JSONP方法

什么是Jsonp? Jsonp(JSON with Padding) 是 json 的一種"使用模式",可以讓網頁從別的域名(網站)那獲取資料,即跨域讀取數據。

JSONP的原理其實就是利用引入script不限制源的特點,把處理函數名作為參數傳入,然后返回執行語句。

在js中,我們直接用XMLHttpRequest請求不同域上的數據時,是不可以的。但是,在頁面上引入不同域上的js腳本文件卻是可以的,jsonp正是利用這個特性來實現的。

比如,有個a.html頁面,它里面的代碼需要利用ajax獲取一個不同域上的json數據,假設這個json數據地址是http://aaa.com/data.php,那么a.html中的代碼就可以這樣:

<!-- a.html -->
<script>
    function dealData (data) {
        console.log(data);
    }
</script>

<script src='http://aaa.com/data.php?callback=dealData'></script>

  我們看到獲取數據的地址后面還有一個callback參數,按慣例是用這個參數名,但是你用其他的也一樣。當然如果獲取數據的jsonp地址頁面不是你自己能控制的,就得按照提供數據的那一方的規定格式來操作了。

因為是當做一個js文件來引入的,所以http://aaa.com/data.php返回的必須是一個能執行的js文件,所以這個頁面的php代碼可能是這樣的:

<?php
    $callback = $_GET['callback'];//得到回調函數名;
    $data = 'data';//要返回的數據;
    echo $callback.'('.json_encode($data).')';//輸出。
?>

  如果在jQuery中用JSONP的話就更加簡單了:

<script>
$.getJSON(''http://aaa.com/data.php?callback=?', function (data) {
    console.log(data);
});
</script>

  

 注意jQuery會自動生成一個全局函數來替換callback=?中的問號,之后獲取到數據后又會自動銷毀,實際上就是起一個臨時代理函數的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就調用普通的ajax方法;跨域的話,則會以異步加載js文件的形式來調用JSONP的回調函數。

 

window.name方法

關鍵點:window.name在頁面的生命周期里共享一個window.name;
兼容性:所有瀏覽器都支持;
優點:
 最簡單的利用了瀏覽器的特性來做到不同域之間的數據傳遞;
 不需要前端和后端的特殊配制;
缺點:
 大小限制:window.name最大size是2M左右,不同瀏覽器中會有不同約定;
 安全性:當前頁面所有window都可以修改,很不安全;
 數據類型:傳遞數據只能限於字符串。

設想場景:在一個頁面 aaa.com/3a.html 中,我們想獲取 bbb.com/3b.html 中的數據,以下是解決方案:

<!-- 3b.html -->
<script>
window.name = '3a.html想要的3b.html里面的數據'; //這是就是我們需要通信的數據
</script>

  

<!-- 3a.html -->
<html>
<head>
<script>
    function getData () {
        var iframe = document.getElementById('iframe');
        iframe.src = 'bbb.com/3b.html'; // 這里讓iframe與父頁面同源
        
        iframe.onload = function () {
            var data = iframe.contentWindow.name; //在這里我們得到了跨域頁面中傳來的數據
        };
    }
</script>
</head>
<body>
<iframe id="iframe" src = 'bbb.com/3b.html' onload()="getData ()">
</iframe> 

</body> </html>

  

在3a.html頁面中使用一個隱藏的iframe來充當一個中間人角色,由iframe去獲取3b.html的數據,然后a.html再去得到iframe獲取到的數據。充當中間人的iframe想要獲取到3b.html的通過window.name設置的數據,只需要把這個iframe的src設為bbb.com/3b.html就行了。然后3a.html想要得到iframe所獲取到的數據,也就是想要得到iframe的window.name的值,還必須把這個iframe的src設成跟3a.html頁面同一個域才行,不然根據前面講的同源策略,3a.html是不能訪問到iframe里的window.name屬性的。這就是整個跨域過程。

 

document.domain方法

設想場景:有一個頁面 http://www.example.com/a.html ,它里面有一個iframe,這個iframe的源是 http://example.com/b.html ,很顯然它們是不同源的,所以我們無法在父頁面中操控子頁面的內容。

解決方案:

<!-- b.html -->
<script>
document.domain = 'example.com';
</script>

  

<!-- a.html -->
<script>
document.domain = 'example.com';
var iframe = document.getElementById('iframe').contentWindow.document;

//后面就可以操作iframe里的內容了...

</script>

  

window.postMessage方法

window.postMessage(message,targetOrigin)  方法是html5新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬於同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。

需要接收消息的window對象,可是通過監聽自身的message事件來獲取傳過來的消息,消息內容儲存在該事件對象的data屬性中。

上面所說的向其他window對象發送消息,其實就是指一個頁面有幾個框架的那種情況,因為每一個框架都有一個window對象。在討論第二種方法的時候,我們說過,不同域的框架間是可以獲取到對方的window對象的,而且也可以使用window.postMessage這個方法。下面看一個簡單的示例,有兩個頁面

<!-- http://test.com/a.html -->
<script>
    function onLoad(){
    var  iframe=document.getElementById('iframe');
    var win =iframe.contentWindow;
    win.postMessage('this is message from a.html','*');
}
</script>
<iframe id="iframe" src="http://www.test.com/b.html" onload="onLoad()"></iframe>

  

<!-- http://test.com/b.html -->

<script>
	window.onmessage=function(e){  //注冊message事件用來接收消息
    e =e||event;//獲取事件對象
    alert(e.data);
    
    
}    

</script>

  使用postMessage來跨域傳送數據還是比較直觀和方便的,但是缺點是IE6、IE7不支持,所以用不用還得根據實際需要來決定。


免責聲明!

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



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