韋陀掌法,難陀時間善惡;梯雲縱,難縱過亂世紛擾。javascript
如今開始寫代碼o(╯□╰)ohtml
廣義的跨域是指一個域下對的文檔或者腳本試圖去請求另一個域下的資源。前端
- a連接、重定向、表單提交
<frame>、<link>、<script>、<img>
等標籤background:url()、@font-face()
- ajax 跨域請求
- ……
狹義的跨域是指瀏覽器同源策略限制的一類請求場景。java
原理:動態建立<script>
標籤,而後利用<script>
的src不受同源策略約束來跨域獲取數據。jquery
缺點:只支持get方式請求nginx
var script = document.createElement('script');
script.type = 'text/javascript';
// 傳參一個回調函數名給後端,方便後端返回時執行這個在前端定義的回調函數
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=jsonPCallback';
document.head.appendChild(script);
// 前端回調執行函數
function jsonPCallback(res) {
alert(JSON.stringify(res));
}
//服務端返回以下(後端返回執行函數):
jsonPCallback({"status": true, "user": "admin"})
複製代碼
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 請求方式爲jsonp
jsonpCallback: "handleCallback", // 自定義回調函數名
data: {}
});
複製代碼
CORS須要瀏覽器和服務器同時支持。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。ajax
整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。
與jsonp相比:支持全部類型的HTTP請求。但JSONP支持老式瀏覽器。json
(1) 請求方法是如下三種方法之一:後端
- HEAD
- GET
- POST
(2)HTTP的頭信息不超出如下幾種字段:跨域
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
同時知足以上兩個條件的就是簡單請求
//簡單請求,瀏覽器自動增長Origin字段,Origin字段用來講明,本次請求來自哪一個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求。
Content-Type: text/plain
Origin: http://www.domain.com
User-Agent: Mozilla/5.0
//若是Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP迴應。瀏覽器檢查這個響應的頭信息有沒有包含Access-Control-Allow-Origin字段,沒有的話,就會拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲。注意,這種錯誤沒法經過狀態碼識別,由於HTTP迴應的狀態碼有多是200。
//若是Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段。
Access-Control-Allow-Origin: http://www.domain.com
//值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求。
Access-Control-Allow-Credentials: true
//值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器。這個值也只能設爲true,若是服務器不要瀏覽器發送Cookie,刪除該字段便可。
同時,請求中也要設置
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
//Access-Control-Allow-Origin此時不能設爲星號
Access-Control-Expose-Headers: FooBar
//用於拿到XMLHttpRequest對象非基本字段
複製代碼
//與簡單請求不一樣的是,複雜請求多了2個字段,進行服務器預檢:
Access-Control-Request-Method:該次請求的請求方式
Access-Control-Request-Headers:該次請求的自定義請求頭字段
//預檢成功,服務器返回的響應
//指定容許其餘域名訪問
'Access-Control-Allow-Origin: //是否容許後續請求攜帶認證信息(cookies),該值只能是true,不然不返回 'Access-Control-Allow-Credentials:true' //預檢結果緩存時間 'Access-Control-Max-Age: 1800' //容許的請求類型 'Access-Control-Allow-Methods:GET,POST,PUT,POST' //容許的請求頭字段 'Access-Control-Allow-Headers:x-requested-with,content-type' 複製代碼
window.name有如下特徵:
- 每一個窗口都有獨立的window.name與之對應;
- 在一個窗口被關閉前,窗口載入的全部頁面同時共享一個window.name,每一個頁面對window.name都有讀寫的權限;
- window.name一直存在與當前窗口,即便是有新的頁面載入也不會改變window.name的值;
- window.name能夠存儲最多2M的數據,數據格式按需自定義。
原理:在頁面中動態建立一個iframe頁面指向另外一個域,將數據賦值給ifram的window.name屬性。(此時頁面不能直接讀取iframe的window.name),咱們還須要將將iframe的src指向相同域的空白頁面。以後再將iframe刪除就能夠了
<!--a.html-->
var proxy = function(url, callback) {
var state = 0;
var iframe = document.createElement('iframe');
// 加載跨域頁面
iframe.src = url;
// onload事件會觸發2次,第1次加載跨域頁,並留存數據於window.name
iframe.onload = function() {
if (state === 1) {
// 第2次onload(同域proxy頁)成功後,讀取同域window.name中數據
callback(iframe.contentWindow.name);
destoryFrame();
} else if (state === 0) {
// 第1次onload(跨域頁)成功後,切換到同域代理頁面
iframe.contentWindow.location = 'http://www.domain.com/aa.html';
state = 1;
}
};
document.body.appendChild(iframe);
// 獲取數據之後銷燬這個iframe,釋放內存;這也保證了安全(不被其餘域frame js訪問)
function destoryFrame() {
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
}
};
// 請求跨域b頁面數據
proxy('http://www.domain1.com/b.html', function(data){
alert(data);
});
<!--b.html-->
<script> window.name = 'This is domain1 data!'; </script>
複製代碼
主域相同,子域不一樣的跨域應用場景。 原理:兩個頁面都經過js強制設置document.domain爲基礎主域,就實現了同域。
<!--a.html-->
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script> document.domain = 'domain.com'; var user = 'admin'; </script>
<!--b.html-->
<script> document.domain = 'domain.com'; // 獲取父窗口中變量 alert('get js data from parent ---> ' + window.parent.user); </script>
複製代碼
location.hash:指的是URL的#後面的部分,好比www.domain1.com/b.html#hello 的#hello,只改變hash是不會刷新頁面。 原理:經過中間頁面來實現。 三個頁面,不一樣域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通訊。
<!--http://www.domain.com/a.html-->
<iframe id="iframe" src="http://www.domain1.com/b.html" style="display:none;"></iframe>
<script> var iframe = document.getElementById('iframe'); // 向b.html傳hash值 setTimeout(function() { iframe.src = iframe.src + '#user=admin'; }, 1000); // 開放給同域c.html的回調方法 function onCallback(res) { alert('data from c.html ---> ' + res); } </script>
<!--http://www.domain1.com/b.html-->
<iframe id="iframe" src="http://www.domain.com/c.html" style="display:none;"></iframe>
<script> var iframe = document.getElementById('iframe'); // 監聽a.html傳來的hash值,再傳給c.html window.onhashchange = function () { iframe.src = iframe.src + location.hash; }; </script>
<!--http://www.domain.com/c.html-->
<script> // 監聽b.html傳來的hash值 window.onhashchange = function () { // 再經過操做同域a.html的js回調,將結果傳回 window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', '')); }; </script>
複製代碼
otherWindow.postMessage(message, targetOrigin, [transfer]);
window.postMessage() 方法被調用時,會在全部頁面腳本執行完畢以後(e.g., 在該方法以後設置的事件、以前設置的timeout 事件,etc.)向目標窗口派發一個 MessageEvent 消息。 該MessageEvent消息有四個屬性須要注意: message 屬性表示該message 的類型; data 屬性爲 window.postMessage 的第一個參數;origin 屬性表示調用window.postMessage() 方法時調用頁面的當前狀態; source 屬性記錄調用 window.postMessage() 方法的窗口信息。 優點:頁面和其打開的新窗口的數據傳遞、 多窗口之間消息傳遞、嵌套的iframe消息傳遞的信息傳遞
<!--http://www.domain.com/a.html-->
<iframe id="iframe" src="http://www.domain1.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain1傳送跨域數據
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain1.com');
};
// 接受domain返回數據
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>
<!--http://www.domain1.com/b.html-->
<script>
// 接收domain的數據
window.addEventListener('message', function(e) {
alert('data from domain ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// 處理後再發回domain
window.parent.postMessage(JSON.stringify(data), 'http://www.domain.com');
}
}, false);
</script>
複製代碼
若是您不但願從其餘網站接收message,請不要爲message事件添加任何事件偵聽器。
若是您確實但願從其餘網站接收message,請始終使用origin和source屬性驗證發件人的身份。
當您使用postMessage將數據發送到其餘窗口時,始終指定精確的目標origin,而不是*
請看以前的文章 前端如何玩轉Nginx
中間件代理跨域相關教程,請關注冰山工做室」中間件系列教程「,敬請期待~