2018/06/13 重新編輯排版
什么是跨域?
同源策略是由 Netscape 提出的著名安全策略,是瀏覽器最核心、基本的安全功能,它限制了一個源(origin)中加載文本或者腳本與來自其他源(origin)中資源的交互方式
,所謂的同源就是指協議、域名、端口相同。
當瀏覽器執行一個腳本時會檢查是否同源,只有同源的腳本才會執行,如果不同源即為跨域
跨域的幾種方式
在實際項目中,經常會遇到在一個域名下請求另外一個域名的資源的情況,這就會涉及到跨域問題,下面我們來探討下跨域的幾種實現方式(下面所有例子中,后端使用 node.js,框架 express):
1、jsonp
最常見的一種跨域方式,其背后原理就是利用了 script 標簽不受同源策略的限制,在頁面中動態插入了 script,script 標簽的 src 屬性就是后端 api 接口的地址,并且以 get 的方式將前端回調處理函數名稱告訴后端,后端在響應請求時會將回調返還,并且將數據以參數的形式傳遞回去。
前端:
// http://127.0.0.1:8888/jsonp.html
var script = document.createElement('script');
script.src = 'http://127.0.0.1:2333/jsonpHandler?callback=_callback';
document.body.appendChild(script); //插入script標簽
// 回調處理函數 _callback
var _callback = function(obj) {
for(key in obj) {
console.log('key: ' + key +' value: ' + obj[key]);
}
}
后端:
// http://127.0.0.1:2333/jsonpHandler
app.get('/jsonpHandler', (req, res) => {
let callback = req.query.callback;
let obj = {
type: 'jsonp',
name: 'weapon-x'
};
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(callback + '(' + JSON.stringify(obj) + ')');
})
在 jsonp.html 中打開控制臺可以看到返回數據的輸出:
2、CORS
Cross-Origin Resource Sharing(跨域資源共享)是一種允許當前域(origin)的資源(比如html/js/web service)被其他域(origin)的腳本請求訪問的機制。
當使用 XMLHttpRequest 發送請求時,瀏覽器如果發現違反了同源策略就會自動加上一個請求頭 origin,后端在接受到請求后確定響應后會在 Response Headers 中加入一個屬性 Access-Control-Allow-Origin,值就是發起請求的源地址(http://127.0.0.1:8888),瀏覽器得到響應會進行判斷 Access-Control-Allow-Origin 的值是否和當前的地址相同,只有匹配成功后才進行響應處理。
現代瀏覽器中和移動端都支持 CORS(除了opera mini),IE 下需要8+
前端:
// http://127.0.0.1:8888/cors.html
var xhr = new XMLHttpRequest();
xhr.onload = function(data) {
var _data = JSON.parse(data.target.responseText)
for(key in _data) {
console.log('key: ' + key + ' value: ' + _data[key]);
}
};
xhr.open('POST','http://127.0.0.1:2333/cors',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send();
后端:
// http://127.0.0.1:2333/cors
app.post('/cors', (req, res) => {
if(req.headers.origin) {
res.writeHead(200, {
"Content-Type": "text/html; charset=UTF-8",
"Access-Control-Allow-Origin": 'http://127.0.0.1:8888'
});
let people = {
type: 'cors',
name: 'weapon-x'
}
res.end(JSON.stringify(people));
}
})
可以在開發者工具里面看到請求的詳細信息,并且在控制臺也可以看到返回的數據輸出:
3、服務器跨域
在前后端分離的項目中可以借助服務器實現跨域,具體做法是:前端向本地服務器發送請求,本地服務器代替前端再向真實服務器接口發送請求進行服務器間通信,本地服務器其實充當個「中轉站」的角色,再將響應的數據返回給前端,來看下實際例子:
前端:
// http://127.0.0.1:8888/server
var xhr = new XMLHttpRequest();
xhr.onload = function(data) {
var _data = JSON.parse(data.target.responseText)
for(key in _data) {
console.log('key: ' + key +' value: ' + _data[key]);
}
};
xhr.open('POST','http://127.0.0.1:8888/feXhr',true); // 向本地服務器發送請求
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send("url=http://127.0.0.1:2333/beXhr"); // 以參數形式告知需要請求的后端接口
后端:
// http://127.0.0.1:8888/feXhr
app.post('/feXhr', (req, res) => {
let url = req.body.url;
superagent.get(url) //使用 superagent 向實際接口發起請求
.end((err, docs) => {
if(err) {
console.log(err);
return
}
res.end(docs.res.text); // 返回給前端
})
})
// http://127.0.0.1:2333/beXhr
app.get('/beXhr', (req, res) => {
let obj = {
type: 'superagent',
name: 'weapon-x'
};
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(JSON.stringify(obj)); //響應
})
回到 http://127.0.0.1:8888/server 頁面打開控制臺可以看到數據輸出:
4、postmessage跨域
在 HTML5 中新增了 postMessage 方法,postMessage 可以實現跨文檔消息傳輸 Cross Document Messaging,IE8,Firefox 3,Opera 9,Chrome 3 和 Safari 4 都支持 postMessage。
該方法可以通過綁定 window 的 message 事件來監聽發送跨文檔消息傳輸內容。
使用 postMessage 實現跨域的話原理就類似于 jsonp,動態插入 iframe標簽,再從 iframe 里面拿回數據
,私認為用作跨頁面通信更加適合
總結
當然還有其他實現跨域的方式比如在ie8、9下 XDR 跨域方案,flash 方案,以上是一些常用的跨域方案,都各有利弊,比如:jsonp 只能發送 get 請求、服務器跨域需要另起服務器等等,大家可以根據自己項目需求選擇適合的解決方案,如果對于跨域還有其他看法或者文中出現錯誤,歡迎大家留言 :D
參考文獻: