同源策略
瀏覽器出于安全方面的考慮,只允許與本域下的接口交互。不同源的客戶(hù)端腳本在沒(méi)有明確授權(quán)的情況下,不能讀寫(xiě)對(duì)方的資源。
本域指的是:
- 同域名:例如都是google.com/a 和google.com/b
- 同端口:例如都是80端口,(如果沒(méi)有寫(xiě)端口,默認(rèn)是80端口)
- 同協(xié)議:例如都是http或者h(yuǎn)ttps
需要注意的是: 對(duì)于當(dāng)前頁(yè)面來(lái)說(shuō)頁(yè)面存放的 JS 文件的域不重要,重要的是加載該 JS 頁(yè)面所在什么域
跨域的實(shí)現(xiàn)方式
JSONP
JSONP是通過(guò)script標(biāo)簽加載數(shù)據(jù)的方式獲取數(shù)據(jù)當(dāng)做JS代碼來(lái)執(zhí)行,提前在頁(yè)面上聲明一個(gè)函數(shù),函數(shù)名通過(guò)接口傳參的方式傳給后臺(tái),后臺(tái)解析到函數(shù)名后在原始數(shù)據(jù)上 ‘ 包裹 ’ 這個(gè)函數(shù)名發(fā)送給前端。換句話(huà)說(shuō),JSONP需要對(duì)應(yīng)接口的后端的配合才能實(shí)現(xiàn)。
function fn(data){
console.log(data);
}
<script src="http://api.jirengu.com/seather.php?callback=fn"></script>
假設(shè)后端返回?cái)?shù)據(jù)是{weather:'晴天'},在與后端協(xié)商之后讓后端放回?cái)?shù)據(jù)為fn({'weather':'晴天'});
如上代碼,當(dāng)請(qǐng)求到達(dá)后端后,后端會(huì)去解析callback這個(gè)參數(shù)獲取到字符串fn,前端script標(biāo)簽在加載數(shù)據(jù)后會(huì)把fn({'weather':'晴天'})作為js執(zhí)行,這實(shí)際上就是調(diào)用fn這個(gè)函數(shù),同時(shí)參數(shù)是{'weather':'晴天'},所以只需要在加載前在頁(yè)面提前定義好fn這個(gè)全局函數(shù),在函數(shù)內(nèi)部處理參數(shù)即可。
CORS
CORS 全稱(chēng)是跨域資源共享(Cross-Origin Resource Sharing),是一種 ajax 跨域請(qǐng)求資源的方式,支持現(xiàn)代瀏覽器,IE支持10以上。 實(shí)現(xiàn)方式很簡(jiǎn)單,當(dāng)你使用 XMLHttpRequest 發(fā)送請(qǐng)求時(shí),瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin,后臺(tái)進(jìn)行一系列處理,如果確定接受請(qǐng)求則在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin; 瀏覽器判斷該相應(yīng)頭中是否包含 Origin 的值,如果有則瀏覽器會(huì)處理響應(yīng),我們就可以拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回,這時(shí)我們無(wú)法拿到響應(yīng)數(shù)據(jù)。所以 CORS 的表象是讓你覺(jué)得它與同源的 ajax 請(qǐng)求沒(méi)啥區(qū)別,代碼完全一樣。
var http = require('http');
var path = require('path');
var fs = require('fs');
var url = require('url');
http.createServer(function(req,res){
var pathObj = url.parse(req.url,true);
});
switch (pathObj.pathname) {
case 'getNews':
var news = [
'news1',
'news2'
];
//在響應(yīng)頭加上該屬性,
//瀏覽器會(huì)去比較Oringin是否一致,一致則允許請(qǐng)求,得到該數(shù)據(jù),不一樣就拒絕
//*星號(hào)表示任何人都可以使用該數(shù)據(jù)
res.setHeader('Access-Control-Allow-Origin','*');
res.end(JSON.stringify(news));
break;
default:
fs.readFile(path.join(_dirname,pathObj.pathname), function (e,data){
if(e){
res.writeHead(404,'not found');
res.end('<h1>404 Not Found</h1>');
}else{
res.end(data);
}
}).listen(8080);
}
降域
對(duì)于frame;一般情況下dom是無(wú)法獲取操作不同域下的frame數(shù)據(jù),如果有需求,可以使用降域的方式來(lái)實(shí)現(xiàn),不過(guò)降域?qū)崿F(xiàn)是有條件限制的,舉例來(lái)說(shuō),
a.google.com這個(gè)頁(yè)面中有一個(gè)iframe地址是b.google.com,那么我們可以這樣實(shí)現(xiàn)降域,如下:
<script>
//實(shí)現(xiàn)降域
document.domain = 'google.com';
</script>
postMessage
如果域名完全不同的情況下需要操作frame,可以使用postMessage;
window.postMessage允許多個(gè) window/frame之間跨域傳遞數(shù)據(jù)和信息,該方法可以通過(guò)綁定window的message事件來(lái)監(jiān)聽(tīng)發(fā)送跨文檔消息傳輸內(nèi)容