第一次寫博客,好緊張,不知道能寫成啥樣,哈哈哈。
自己的一知片解,有錯請多多指教,嘻嘻嘻。
一、何為跨域?
只要協議、域名、端口后任何一個不同,就是跨域。
舉個例子:
| http://www.example.com | 協議不同 |
| https://www.example.com | |
| http://www.example.com | 域名不同 |
| http://www.test.com | |
| http://www.example.com | 端口不同 |
| http://www.example.com:81 |
注意:ip相同,域名不同,也是跨域。(在本地寫demo時,配置host文件,127.0.0.1配置了兩個域名,糾結是否算跨域,試了一下,是的)
二、跨域的類型
1.cookie
只有同源的網頁才能共享cookie
2.iframe
網格網頁不同源,無法拿到對方的DOM
3.ajax
只能請求同源的網址,否則報錯
三、解決跨域
我在我電腦的host文件里配置了兩個域名模擬跨域。

以下,test1代表www.test1.myhost.com; test2代表www.test2.myhost.com
1.cookie
存在這樣一種情況能夠實現共享cookie:兩個網頁一級域名形同,二級域名不同,設置cookie時,指定兩個網頁的domain相同,便可以共享cookie啦。
test1:
document.cookie = 'key1= value1; domain=myhost.com';
console.log(document.cookie);//"key1 = value1"
test2:
console.log(document.cookie);//"key1 = value1"
就醬紫,他倆共享cookie了,好可怕
打開application看看,發現這cookie原來在myhost.com域名下。

2.iframe
一個頁面中嵌套一個iframe,這個iframe的src與主頁面跨域。這樣的話,主頁面獲取操作iframe的DOM,iframe也無法操作主頁面的DOM
test1為主頁面,test2為嵌入的iframe
解決iframe跨域的,寶寶只知道這三種:
(1)片段識別符
(2)window.name
(3)window.postMessage
2.1 片段識別符
指的是URL中#后面的部分,只改變片段識別符,頁面不會重新刷新。
實現思路:
父窗口可以把要傳遞的數據寫入iframe的src片段識別符中,iframe通過監聽hashchange事件得到通知,獲取數據
同樣道理,子窗口也可以改變父窗口的片段識別符,達到同樣的效果。示例來嘍:
父→子:
主頁面
var origin = $('iframe').attr('src'); $('iframe').attr('src', origin + '#主窗口');
iframe
console.log(window.location.hash);//"#主窗口"
子→父:
iframe
parent.location.href ='http://www.test1.myhost.com:8080/tutor/cookie#iframe'
主頁面
console.log(window.location.hash);//"#iframe"
2.2 window.name
瀏覽器窗口有window.name屬性,它的特點是無論是否同源,只要在同一個窗口中,前一個網頁設置了這個屬性,后一個網頁就可以讀取它
實現思路:
將iframe需要與主頁面傳遞的數據寫到iframe的window.name中,完成后將iframe的src設置成與主頁面同源,此時,主頁面與iframe就同源了,就可以拿到對方的數據啦,而iframe的src更改並不影響window.name的值啊,主頁面便可以輕松讀取iframe中window.name的值啦,是不是很機智!再來個例子吧。
iframe
window.name = '哈哈哈,我是iframe';
location.href = 'http://www.test1.myhost.com:8080/這里就是一個與主頁面同源的頁面';
主頁面
console.log($('iframe')[0].contentWindow.name);//"哈哈哈,我是iframe"
2.3 window.postMessage
html5引入的新API,允許跨窗口通信,不論這兩個窗口是否同源。
window.postMessage(data, url); //data為要傳給目標的數據,url為目標的url。
注意,此window是目標窗口的window,不是本窗口的window
同樣來個例子:
子→父:
iframe
top.postMessage('hello', 'http://www.test1.myhost.com:8080/tutor/cookie');
主頁面
window.addEventListener('message', function(e) {
console.log(e.data);//"hello"
}, false);
父→子:
主頁面
$('.post').on('click', function() {
$('iframe')[0].contentWindow.postMessage('hello hello', 'http://www.test2.myhost.com:8080/這里就是一個目標url');
});
iframe
window.addEventListener('message', function(e) {
console.log(e.data);//"hello hello"
}, false);
這里的e有幾個重要屬性:
a.data:傳遞來的message
b.source:發送消息的窗口對象
c.origin:發送消息窗口的源(協議+主機+端口號)
3.ajax
誒嘿,寶寶知道的也是三種,哈哈哈,好巧啊
(1)JSONP
(2)websocket
(3)CORS
分別說說吧
3.1 JSONP
原理就是利用script腳本擁有跨域能力。
基本思想是,網頁添加一個script元素,src放需要請求的接口,這種做法不受同源策略的限制。服務器收到請求后,將數據放在一個指定的回調函數里傳回來。
這種方法簡單適用,老式的瀏覽器全部支持,服務器改造也非常小。
js實現:
function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } $('.ajax').on('click', function() { addScriptTag('http://www.php.myhost.com/jsonp.php?callback=foo'); }); function foo(data) { console.log('response data is: ' + data); };
jQuery實現:
$('.ajax').on('click', function() {
$.ajax({
url: 'http://www.php.myhost.com/jsonp.php',
type: 'get',
dataType: 'jsonp',
jsonpCallback: 'foo',
data: {}
});
});
function foo(data) {
console.log('Your response is: ' + data);
};
PHP代碼:
<?php $callback = $_GET['callback']; $data = 'hello'; echo $callback.'('.json_encode($data).')'; ?>
3.2 websocket
websocket是一種通信協議。該協議不實行同源策略,只要服務器支持,就可以通過它進行跨源通信。
例如,長連接呀
其實不太懂,就先不說它了哈
3.3 CORS
開始扯啦
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標准,是跨源AJAX請求的根本解決方法。
CORS請求分為兩類:簡單請求和非簡單請求,太多了,這里就先不介紹了,一查就知道了。
簡單請求就是瀏覽器發出CORS請求時,http頭當中增加一個域(origin)的信息。該域包含協議名、地址及一個可選的端口,用來說明本次請求來自哪個源,服務器根據這個值決定是否同意這次請求。(這都是瀏覽器代為發送,開發者的代碼無法觸及到)
如果這個指定的源不在許可范圍內,服務器就返回一個正常的http回應。但是回應中沒有Access-Control-Allow-Origin字段,拋出異常,XMLHttpRequest的onerror捕獲。
如果這個指定的源在許可范圍內,會多出幾個頭信息字段(都以Access-Contrl-開頭)
非簡單請求(我平常請求的接口都是非簡單請求,因為content-type為application/json)是在發起正式通信之前增加一次http查詢,實現詢問服務器當前網頁所在域名是否在許可名單中,得到肯定答復后瀏覽器才會發出正式的XMLHttpRequest請求,否則報錯。
預檢請求的方式是options,頭信息里面關鍵字段是origin,表示來自哪個源。服務器收到預檢請求后檢查origin,確認是否允許跨源請求,就可以做出回應了。
允許請求:在http回應中,關鍵是Access-Control-Allow-Origin字段,如:Access-Control-Allow-Origin:http://api.com,表示http://api.com可以請求數據。設為*,表示同意任何跨域。
不允許請求:會返回一個正常的http回應,但沒有任何CORS相關的頭信息字段,瀏覽器會認為不同意預檢請求,觸發一個錯誤,被XMLHttpRequest的onerror捕獲。
總而言之,普通跨域請求,只要服務端設置Access-Control-Allow-Origin即可,前端無須設置。帶cookie請求,需要前后端都設置字段。注意,所帶cookie為跨域請求接口所在域的cookie,而非當前頁的。
查了一下前端帶cookie請求咋實現:
js
var xhr = new XMLHttpRequest(); // 前端設置是否帶cookie xhr.withCredentials = true;
jquery
$.ajax({ xhrFields: { withCredentials: true // 前端設置是否帶cookie }, });
vue:
vue框架在vue-resource封裝的ajax組件中加入以下代碼: Vue.http.options.credentials = true
ok ,總算寫完啦~~~
哦,對了,加一下ajax跨域的幾種現象(這幾種現象這個博客說的很好:http://www.cnblogs.com/dailc/p/5893341.html#crossDomain_crosPrinciple)
