詳解跨域的幾種方式
小編推薦:Fundebug專注於JavaScript、微信小程式、微信小遊戲,Node.js和Java實時BUG監控。真的是一個很好用的bug監控費服務,眾多大佬公司都在使用。
一、什麼是跨域
JavaScript出於安全方面的考慮,不允許跨域呼叫其他頁面的物件。那什麼是跨域呢,簡單地理解就是因為JavaScript同源策略的限制,a.com域名下的js無法操作b.com或是c.a.com域名下的物件。
當協議、子域名、主域名、埠號中任意一個不相同時,都算作不同域。不同域之間相互請求資源,就算作“跨域”。
例如:http://www.abc.com/index.html 請求
有一點必須要注意:跨域並不是請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。之所以會跨域,是因為受到了同源策略的限制,同源策略要求源相同才能正常進行通訊,即協議、域名、埠號都完全一致。
大家可以參照下圖,有助於深入理解跨域。
特別說明兩點:
第一:如果是協議和埠造成的跨域問題“前臺”是無能為力的。
第二:在跨域問題上,域僅僅是通過“URL的首部”來識別而不會根據域名對應的IP地址是否相同來判斷。“URL的首部”可以理解為“協議, 域名和埠必須匹配”。
二、什麼是同源策略及其限制
同源策略限制從一個源載入的文件或指令碼如何與來自另一個源的資源進行互動。這是一個用於隔離潛在惡意檔案的關鍵的安全機制。它的存在可以保護使用者隱私資訊,防止身份偽造等(讀取Cookie)。
同源策略限制內容有:
- Cookie、LocalStorage、IndexedDB 等儲存性內容
- DOM 節點
- AJAX 請求不能傳送
但是有三個標籤是允許跨域載入資源:
1.<img src=XXX>
2.<link href=XXX>
3.<script src=XXX>
接下來我們討論下有哪些處理跨域的方法。但所有的跨域都必須經過資訊提供方的允許。如果未經允許即可獲取,那是瀏覽器同源策略出現漏洞。
三、處理跨域方法一——JSONP
1.JSONP原理
利用 <script>
元素的這個開放策略,網頁可以得到從其他來源動態產生的 JSON 資料。JSONP請求一定需要對方的伺服器做支援才可以。
2.JSONP和AJAX對比
JSONP和AJAX相同,都是客戶端向伺服器端傳送請求,從伺服器端獲取資料的方式。但AJAX屬於同源策略,JSONP屬於非同源策略(跨域請求)
3.JSONP優缺點
JSONP優點是相容性好,可用於解決主流瀏覽器的跨域資料訪問的問題。缺點是僅支援get方法具有侷限性。
4.JSONP的流程(以第三方API地址為例,不必考慮後臺程式)
- 宣告一個回撥函式,其函式名(如fn)當做引數值,要傳遞給跨域請求資料的伺服器,函式形參為要獲取目標資料(伺服器返回的data)。
-
建立一個
<script>
標籤,把那個跨域的API資料介面地址,賦值給script的src,還要在這個地址中向伺服器傳遞該函式名(可以通過問號傳參:?callback=fn)。- 伺服器接收到請求後,需要進行特殊的處理:把傳遞進來的函式名和它需要給你的資料拼接成一個字串,例如:傳遞進去的函式名是fn,它準備好的資料是fn([{"name":"jianshu"}])。
- 最後伺服器把準備的資料通過HTTP協議返回給客戶端,客戶端再呼叫執行之前宣告的回撥函式(fn),對返回的資料進行操作。
<script type="text/javascript">
function fn(data) {
alert(data.msg);
}
</script>
<script type="text/javascript" src="http://crossdomain.com/jsonServerResponse?jsonp=fn"></script>
其中 fn 是客戶端註冊的回撥的函式,目的獲取跨域伺服器上的json資料後,對資料進行在處理。
最後伺服器返回給客戶端資料的格式為:
fn({ msg:'this is json data'})
5.jQuery的jsonp形式
JSONP都是GET和非同步請求的,不存在其他的請求方式和同步請求,且jQuery預設就會給JSONP的請求清除快取。
$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",//可以省略
jsonpCallback:"fn",//->自定義傳遞給伺服器的函式名,而不是使用jQuery自動生成的,可省略
jsonp:"jsonp",//->把傳遞函式名的那個形參callback變為jsonp,可省略
success:function (data){
console.log(data);}
});
四、處理跨域方法二——CORS
1.CORS原理
整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。
2.CORS優缺點
CORS要求瀏覽器(>IE10)和伺服器的同時支援,是跨域的根本解決方法,由瀏覽器自動完成。
優點在於功能更加強大支援各種HTTP Method,缺點是相容性不如JSONP。
只需要在伺服器端做一些小小的改造即可:
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Methods:POST,GET");
例如:網站http://localhost:63342/ 頁面要請求http://localhost:3000/users/userlist 頁面,userlist頁面返回json字串格{name: 'Mr.Cao', gender: 'male', career: 'IT Education'}
//在伺服器端設定同源策略地址
router.get("/userlist", function (req, res, next) {
var user = {name: 'Mr.Cao', gender: 'male', career: 'IT Education'};
res.writeHeader(200,{"Access-Control-Allow-Origin":'http://localhost:63342'});
res.write(JSON.stringify(user));
res.end();
});
在響應頭上新增Access-Control-Allow-Origin屬性,指定同源策略的地址。同源策略預設地址是網頁的本身。只要瀏覽器檢測到響應頭帶上了CORS,並且允許的源包括了本網站,那麼就不會攔截請求響應。
五、處理跨域方法三——WebSocket
Websocket是HTML5的一個持久化的協議,它實現了瀏覽器與伺服器的全雙工通訊,同時也是跨域的一種解決方案。WebSocket和HTTP都是應用層協議,都基於 TCP 協議。但是 WebSocket 是一種雙向通訊協議,在建立連線之後,WebSocket 的 server 與 client 都能主動向對方傳送或接收資料。同時,WebSocket 在建立連線時需要藉助 HTTP 協議,連線建立好了之後 client 與 server 之間的雙向通訊就與 HTTP 無關了。
原生WebSocket API使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket介面,提供了更簡單、靈活的介面,也對不支援webSocket的瀏覽器提供了向下相容。
//前端程式碼:
<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');
// 連線成功處理
socket.on('connect', function() {
// 監聽服務端訊息
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});
// 監聽服務端關閉
socket.on('disconnect', function() {
console.log('Server socket has closed.');
});
});
document.getElementsByTagName('input')[0].onblur = function() {
socket.send(this.value);
};
</script>
//Nodejs socket後臺:
var http = require('http');
var socket = require('socket.io');
// 啟http服務
var server = http.createServer(function(req, res) {
res.writeHead(200, {
'Content-type': 'text/html'
});
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
// 監聽socket連線
socket.listen(server).on('connection', function(client) {
// 接收資訊
client.on('message', function(msg) {
client.send('hello:' + msg);
console.log('data from client: ---> ' + msg);
});
// 斷開處理
client.on('disconnect', function() {
console.log('Client socket has closed.');
});
});
六、處理跨域方法四——postMessage
如果兩個網頁不同源,就無法拿到對方的DOM。典型的例子是iframe視窗和window.open方法開啟的視窗,它們與父視窗無法通訊。HTML5為了解決這個問題,引入了一個全新的API:跨文件通訊 API(Cross-document messaging)。這個API為window物件新增了一個window.postMessage方法,允許跨視窗通訊,不論這兩個視窗是否同源。postMessage方法的第一個引數是具體的資訊內容,第二個引數是接收訊息的視窗的源(origin),即"協議 + 域名 + 埠"。也可以設為*,表示不限制域名,向所有視窗傳送。
接下來我們看個例子:http://localhost:63342/index.html
頁面向http://localhost:3000/message.html
傳遞“跨域請求資訊”
//傳送資訊頁面 http://localhost:63342/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨域請求</title>
</head>
<body>
<iframe src="http://localhost:3000/users/reg" id="frm"></iframe>
<input type="button" value="OK" onclick="run()">
</body>
</html>
<script>
function run(){
var frm=document.getElementById("frm");
frm.contentWindow.postMessage("跨域請求資訊","http://localhost:3000");
}
</script>
//接收資訊頁面 http://localhost:3000/message.html
window.addEventListener("message",function(e){ //通過監聽message事件,可以監聽對方傳送的訊息。
console.log(e.data);
},false);
七、參考文章
關於Fundebug
Fundebug專注於JavaScript、微信小程式、微信小遊戲、支付寶小程式、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了9億+錯誤事件,得到了Google、360、金山軟體、百姓網等眾多知名使用者的認可。歡迎免費試用!