1.什么是同源策略及限制
1.同源策略限制從一個源加載的文檔或腳本與來自另一個源的資源進行交互继效。 ( 這是一個用于隔離潛在惡意文件的關鍵的安全機制。)
2.一個源包括:協(xié)議捍岳、域名涕烧、端口
(這三個有一個不一樣就是源不一樣,就是我們所說的跨域了)
http:// 協(xié)議
www.xxx.com 域名
沒指名端口默認80
3.限制:不是一個源的文檔沒有權利去操作另一個源的文檔菜谣;
主要限制在幾個方面:
-
Cookie、LocalStorage 和 IndexDB 無法讀取
--操作不了Cookie晚缩、LocalStorage尾膊、IndexDB -
DOM無法獲得
--無法獲取和操作另一個資源的DOM -
Ajax請求不能發(fā)送(同源下的通信方式)
--Ajax只適合同源的通信(跨域就不行了)
2.前后端如何通信
1、Ajax
--同源下的通信方式
2荞彼、WebSocket
--不受同源策略的限制
3冈敛、CORS
--支持跨域通信,也支持同源通信
3.如何創(chuàng)建Ajax
1鸣皂、XMLHttpRequest對象的工作流程
2抓谴、兼容性處理
3、事件的觸發(fā)條件
4寞缝、事件的觸發(fā)順序
function ajax(url,fnSucc,fnFaild){
//1癌压、創(chuàng)建一個對象(兼容IE6寫法)
var xhr = XMLHttpRequest ? new XMLHttpRequest():
new ActiveXObject('Microsoft.XMLHTTP');
//2、連接服務器荆陆,確定對象的發(fā)送方式:xhr.open(type,url,true);
xhr.open('GET',url,true);
//3滩届、發(fā)送請求
xhr.send();
//4、接受服務器返回(響應)
xhr.onreadyStateChange = function(){
if(xhr.readyState == 4){//完成
//如果加載媒體資源需要再加上xhr.status===206
(媒體資源特別大被啼,不是一次性返回過來的帜消,是資源的一部分;服務端給你下發(fā)的http狀態(tài)碼是206浓体,
這個地方不加206泡挺,是收不到響應的)
if (xhr.status === 200 || xhr.status === 304) {
fuSucc(xhr.responseText);
}else{
if(fnFaild){
fnFaild(xhr.status);
}
}
}
};
}
4.跨域通信的幾種方式
1、JSONP
1.在什么時候用:
--在出現(xiàn)postMessage汹碱、CORS之前一直用JSONP做跨域通信的粘衬;
2.怎么做到的:
-- 利用script標簽的異步加載來實現(xiàn)的;
(一個頁面是www.immoc.com咳促,script標簽地址的域名100%不是(js的地址和域名是不一致的)稚新,
跨源了,不影響script加載)--這就是jsonp能夠實行的一個最初的基本原理跪腹;
3.怎么實現(xiàn)的:
--需要給服務端傳遞一個回調的名褂删,這個回調的名就是我用加載script標簽的方式發(fā)出一個請求去,
你給我返回一塊內容冲茸,這個內容是一個js塊也就是script的塊屯阀,這個塊中有回調名加代碼就能運行了缅帘,
--原理:(利用這個發(fā)出請求了,告訴服務端callback的名稱,將來要作為函數(shù)名來返回的难衰,既然是函數(shù)名,
要創(chuàng)建一個函數(shù)钦无,所以在調jsonp的時候 ,本地必須有一個jsonp的這么全局函數(shù)盖袭,后面才能把給的數(shù)據(jù)能
執(zhí)行出來失暂,當函數(shù)來運行。)
callback后面名字叫什么都可以鳄虱;
<script src="http://www.abc.com/?data=name&callback=jsonp"></script>
-告訴它一個回調的名稱,而且要在window注冊一個全局的一個函數(shù)弟塞,然后下面的createScript就是要動
態(tài)創(chuàng)建一個script標簽,最后返回這個東西拙已。最后script.onload是監(jiān)聽腳本的加載事件决记,如果響應完了,
也會響應onload,然后判斷onload是不是成功倍踪,成功了以后看能不能拿到那個數(shù)據(jù)系宫。最后不要忘了刪除這
個函數(shù)變量window[callbackName]=null;最后往html中增加script標簽目的就是把這個請求發(fā)送出去。
3.1.1.服務器給你下發(fā)的是一個script內容建车,利用回調的東西笙瑟,執(zhí)行了后面的代碼
<script>
jsonp({
data:{
}
});
</script>
jsonp.js:
box({name:'xxx'});
function createJs(sUrl){
var oScript = document.createElement('script');
oScript.type = "text/javascript";
oScript.src = sUrl;
document.getElementsByTagName('head')[0].appendChild(oScript);
}
createJs('jsonp.js?callback=box');
function box(json){
alert(json.name);
}
2、Hash(url地址中#后面的東西)
--Hash的變動頁面不會刷新
url中?后面的叫search:search的改變是會刷新頁面的癞志;所以search不能做跨域通信;
// 利用hash框产,場景是當前頁面 A 通過iframe或frame嵌入了跨域的頁面 B凄杯,跨域給B發(fā)消息
// 在A中偽代碼如下:
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';
// 在B中的偽代碼如下
window.onhashchange = function () {
var data = window.location.hash;
};
**跨域給B發(fā)消息:
先拿到B這個窗口的地址src,然后通過hash的方式后面發(fā)一段字符串秉宿,這個字符串可以是通過
完整的json戒突,最后通過json.stringify()把json轉成字符串發(fā)給B,你是發(fā)出去了描睦,B能不能接收
到膊存,B在自己的代碼中增加一個window.onhashchange,這個事件是用來監(jiān)聽你當前頁面的hash有
沒有改變。之前的頁面是src忱叭,現(xiàn)在的頁面加一個hash隔崎,所以對B來說,你的url的hash變化了韵丑,
就可以拿到了爵卒。拿到了以后通過window.location.hash就能拿到hash的具體內容。
window.location.hash可不是拿到一個撵彻,如果hash后面等于data除了A發(fā)送過來的東西還有別的
拼接钓株,回來要特殊處理一下实牡。
3、postMessage(html5新增加的處理跨域通信的)
同源策略的目標就是限制跨域通信轴合,但是實際業(yè)務中又需要跨域通信创坞;
html5中出現(xiàn)了這個標準postMessage,用這個實現(xiàn)跨域通信受葛;
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)發(fā)送信息
Bwindow.postMessage('data', 'http://B.com');
// 在窗口B中監(jiān)聽
Awindow.addEventListener('message', function (event) {
console.log(event.origin);
console.log(event.source);
console.log(event.data);
}, false);
**怎么發(fā)送:
在A窗口题涨,給誰發(fā)送,要選中哪個窗口奔坟,調postMessage這個API携栋,第一個參數(shù)是發(fā)送的數(shù)據(jù)部分,
這里推薦使用字符串格式咳秉;第二個是接收方那個源婉支,*是可以給任何窗口發(fā)送(很多窗口都能接收
到你的信息,這個是不安全的)澜建,推薦的做法是加上一個源向挖;
**發(fā)送對方怎么接受:
B窗口要做哪些事情呢?
就是要監(jiān)聽message事件炕舵,
window.addEventListener('message',響應函數(shù)何之,true/false指定捕獲還是冒泡);
要拿的就是下面這三個參數(shù):
//來判斷發(fā)送者的源,在你的響應程序中咽筋,你要選擇性的接收溶推;比如我只接收來自A.com的
信息,其他的一律不接收奸攻,那么就通過event.origin這個屬性來判斷
console.log(event.origin);
console.log(event.source);//引用A窗口的對象
console.log(event.data);//發(fā)送的消息通過event.data拿到數(shù)據(jù)
4蒜危、WebSocket(不受同源策略限制的,拿來跨域通信正合適)
【參考資料】http://www.ruanyifeng.com/blog/2017/05/websocket.html
1.聲明一個webSocket對象睹耐,這個地方有兩種辐赞,ws、wss區(qū)別一個加密一個非加密硝训,
后面指向服務器的一個地址响委,這樣就建立了相當于JS一個對象來管理這個鏈接。
var ws = new WebSocket('wss://echo.websocket.org');
2.請求發(fā)送出去
ws.onopen = function (evt) {
console.log('Connection open ...');
ws.send('Hello WebSockets!');
};
3.對方給消息怎么接收窖梁,通過這個參數(shù)的data來拿到
ws.onmessage = function (evt) {
console.log('Received Message: ', evt.data);
ws.close();
};
4.最后這個鏈接不用了赘风,中斷了,監(jiān)聽onclose來確定是不是關閉了
ws.onclose = function (evt) {
console.log('Connection closed.');
};
5纵刘、CORS(Ajax一個變種贝次,fetch實現(xiàn)CORS通信的--新出的通信標準,可以理解為支持跨域通信的Ajax)
【參考資料】http://www.ruanyifeng.com/blog/2016/04/cors.html
Ajax是不能發(fā)送跨域通信的彰导,瀏覽器在識別你用Ajax發(fā)送了一個跨域請求的時候蛔翅,它會在你
http頭中加一個orgin敲茄,來允許跨域通信。如果不加這個頭山析,就是一個普通的Ajax堰燎,遇到跨域通信,
瀏覽器就會攔截了(非法的不允許請求)笋轨。
// url(必選)秆剪,options(可選),then就是回調成功爵政,回調類似ES6的promise的寫法仅讽;
catch就是捕獲錯誤。
fetch('/some/url/', {
method: 'get',
}).then(function (response) {
}).catch(function (err) {
// 出錯了钾挟,等價于 then 的第二個參數(shù)洁灵,但這樣更好用更直觀
});
*CROS為什么就能支持跨域的這種通信?
瀏覽器會攔截ajax請求掺出,如果它覺得這個ajax請求是跨域的徽千,它會在http請求中,加一個origin