JavaScript出於安全方面的考慮,不容許跨域調用其餘頁面的對象。那什麼是跨域呢,簡單地理解就是由於JavaScript同源策略的限制, a.com
域名下的js沒法操做 b.com
或是 c.a.com
域名下的對象。javascript
當協議、子域名、主域名、端口號中任意一個不相同時,都算做不一樣域。不一樣域之間相互請求資源,就算做「跨域」。例如:http://www.abc.com/index.html 請求 http://www.efg.com/service.php。php
有一點必需要注意:跨域並非請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。之因此會跨域,是由於受到了同源策略的限制,同源策略要求源相同才能正常進行通訊,即協議、域名、端口號都徹底一致。html
你們能夠參照下圖,有助於深刻理解跨域。前端
特別說明兩點:java
第一:若是是協議和端口形成的跨域問題「前臺」是無能爲力的。web
第二:在跨域問題上,域僅僅是經過「URL的首部」來識別而不會根據域名對應的IP地址是否相同來判斷。「URL的首部」能夠理解爲「協議, 域名和端口必須匹配」。ajax
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。json
能夠說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。跨域
同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。它的存在能夠保護用戶隱私信息,防止身份僞造等(讀取Cookie)。瀏覽器
同源策略限制內容有:
Cookie、LocalStorage、IndexedDB 等存儲性內容
DOM 節點
AJAX 請求不能發送
可是有三個標籤是容許跨域加載資源:
<imgsrc=XXX>
<linkhref=XXX>
<scriptsrc=XXX>
接下來咱們討論下有哪些處理跨域的方法。但全部的跨域都必須通過信息提供方的容許。若是未經容許便可獲取,那是瀏覽器同源策略出現漏洞。
利用 <script>
元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的 JSON 數據。JSONP請求必定須要對方的服務器作支持才能夠。
JSONP和AJAX相同,都是客戶端向服務器端發送請求,從服務器端獲取數據的方式。但AJAX屬於同源策略,JSONP屬於非同源策略(跨域請求)。
JSONP優勢是兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持get方法具備侷限性。
聲明一個回調函數,其函數名(如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'})
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通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。所以,實現CORS通訊的關鍵是服務器。只要服務器實現了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是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.');
});
});
若是兩個網頁不一樣源,就沒法拿到對方的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);