跨域知識點部分總結

NO IMAGE

跨域的限制

  • 無法獲取cookielocalStorageindexDB
  • 無法獲得DOM
  • 無法發送Ajax

關於cookie

共享cookie

cookie,是服務器端寫入瀏覽器端的小段信息,只有同源的網頁才能共享cookie一級域名相同但二級域名不同的網頁只要設置相同的document.domain,就可以共享cookie

補充知識點:

  • 一級域名 qq.com
  • 二級域名 game.qq.comwww.qq.comlol.qq.com
  • 三級域名 lpl.lol.qq.com

上述一段描述來自網上的文章,在此經過測試,提出了部分意見,關於意見是什麼,後面會提到,現在先證明上述描述的不精確性。

驗證的服務器nginx配置:

server {
listen      8080;
server_name  w1.gaopeng.com;
location / {
root   /Users/gaopeng/Sites/w1.gaopeng.com;
index  index.html index.htm;
}
}
server {
listen      8080;
server_name  w2.gaopeng.com;
location / {
root   /Users/gaopeng/Sites/w2.gaopeng.com;
index  index.html index.htm;
}
}

w1.gaopeng.comw2.gaopeng.com文件夾下的index.html內容

<script>
document.domain = "gaopeng.com";
</script>

主要目的是設置相同的document.cookie

瀏覽器打開w1.gaopeng.com,發現document.domain已經被設置gaopeng.com,此時增加一個cookie

跨域知識點部分總結

瀏覽器再打開w2.gaopeng.com
發現document.domain已經被設置gaopeng.com,但依舊無法獲取上述設置的cookiegao=peng

跨域知識點部分總結

此時我們看下cookie存在的表現:

跨域知識點部分總結

我們發現在w1.gaopeng.com設置的cookieDomain=w1.gaopeng.com

跨域知識點部分總結

發現w2.gaopeng.com依然沒有上述設置的cookie


突然想了一下如果將cookie設置為二級域名會怎麼樣呢?
w1.gaopeng.com下,設置cookie

跨域知識點部分總結

此時會發現在w2.gaopeng.com下,會獲取到剛剛設置的cookie

跨域知識點部分總結

再次觀察w1.gaopeng.comw2.gaopeng.comcookie

跨域知識點部分總結

跨域知識點部分總結

發現第二次設置的cookie共享了

因此得出了結論,cookie的共享與網站的是否同源並無明顯的關係,能否共享應該看cookie自身的domain,如果domain相同,就可以共享

猜想:存在兩個站點不同源(協議、端口不同)

A站:https://www.example.com:3443/

B站:http://www.example.com:3445/

由於cookiedomain都是www.example.com,那麼這兩個頁面的cookie是可以共享的

關於獲取DOM

iframewindow.open

只有同源的網頁才能獲取DOM,一級域名相同但二級域名不同的網頁只要設置相同的document.domain,就獲取DOM

// w1.gaopeng.com/index.html
index01.html
<iframe id="myIFrame" src="http://w2.gaopeng.com:8080/"></iframe>
<script>
// document.domain = 'gaopeng.com';
// 如果去掉註釋就可以獲取到DOM
</script>
// w2.gaopeng.com/index.html
index02.html
<script>
document.domain = "gaopeng.com";
</script>

跨域知識點部分總結

通過window.open的實例:

跨域知識點部分總結

LocalStorage、IndexDB

利用上述方式的document.domain等特性都無法滿足他們的通信,可以使用下面的介紹的通信,方式,在文章最後會介紹localStorage進行完全不同源的網站之間的通信。

非AJAX並且完全不同源的網站通信

完全不同源的nginx配置

server {
listen      8080;
server_name  w2.gaopeng.com;
location / {
root   /Users/gaopeng/Sites/w2.gaopeng.com;
index  index.html index.htm;
}
}
server {
listen      8092;
server_name  w2.gaopeng1.com;
location / {
root   /Users/gaopeng/Sites/w2.gaopeng1.com;
index  index.html index.htm;
}
}

在這裡由於iframewindow.open的機理類似,所以只進行某一個種方式的演示

片段標識符(iframe)

片段標識符是指URL後面的#號部分,如果只是改變片段標識符,頁面不會重新刷新。

父窗口可以把信息,寫入子窗口的片段標識符。

// w2.gaopeng1.com/index.html
index02.html
<button id='button'>改變hash</button>
<iframe id="myIFrame" src="http://w2.gaopeng1.com:8092/"></iframe>
<script>
var srcUrl  = "http://w2.gaopeng1.com:8092/#" + "gao=peng"
document.getElementById('button').onclick = function () {
document.getElementById('myIFrame').src = srcUrl;
}
</script>
// w2.gaopeng1.com/index.html
demo03.html
<script>
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
console.log('message', message);
}
</script>

如圖所示,當點擊改變hash按鈕時,iframe頁面會檢測到hash的變化執行checkMessage方法

跨域知識點部分總結

window.name(iframe)

瀏覽器窗口有一個window.name,這個屬性的最大特點是,無論是否同源,只要在同一個窗口裡,前一個網頁設置了這個屬性,後一個網頁可以讀取它。

// w2.gaopeng1.com/index.html
index02.html
<iframe id="myIFrame" src="http://w2.gaopeng1.com:8092/"></iframe>
<script>
console.log(window.name);
</script>
// w2.gaopeng1.com/index.html
demo03.html
<script>
window.name = "gao=peng";
</script>

如圖所示,主窗口活動了子窗口設置的window.name

跨域知識點部分總結

window.postMessage(window.open)

上述的兩種方法都是投機取巧的方式,在HTML5中正式引入了:跨文檔通信 API

// w2.gaopeng1.com/index.html
index02.html
<iframe id="myIFrame" src="http://w2.gaopeng1.com:8092/"></iframe>
<script>
// 獲取子窗口的引用
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
win.postMessage(JSON.stringify({key: 'storage', data: obj}), 'http://w2.gaopeng1.com:8092/');
</script>
// w2.gaopeng1.com/index.html
demo03.html
<script>
window.onmessage = function(e) {
if (e.origin !== 'http://w2.gaopeng1.com:8092/') return;
var payload = JSON.parse(e.data);
switch (payload.method) {
case 'set':
localStorage.setItem(payload.key, JSON.stringify(payload.data));
break;
case 'get':
// 獲取父窗口的引用
var parent = window.parent;
var data = localStorage.getItem(payload.key);
parent.postMessage(data, 'http://w2.gaopeng.com:8080/');
break;
case 'remove':
localStorage.removeItem(payload.key);
break;
}
};
</script>

AJAX的跨域通信

解決方案:

  • JSONP
  • WebSocket
  • CORS

主要談論一下CORScookie的影響

跨域知識點部分總結

跨域知識點部分總結

如上面兩張圖對比所示,當發起Ajax請求時:

  1. ajax會自動帶上同源的cookie,不會帶上不同源的cookie
  2. 可以通過前端設置withCredentialstrue, 後端設置Header的方式來讓ajax自動帶上不同源的cookie,但是這個屬性對同源請求沒有任何影響
  3. 如果ajax請求設置withCredentialstrue,注意: Access-Control-Allow-Origin必須制定特定的URL,不能是*, 且需要加上Access-Control-Allow-Credentials
    前端代碼:
// 客戶端也需要設置credentials: "include",不然服務端設置的cookie,也發送不過來
fetch("http://localhost:3000/post_form3", { method: "post", body: formData, credentials: "include" }).then(function(response) {
return response.json();
}).then(function(data) {
console.log(data);
}).catch(function(e) {
console.log(e);
});

後端代碼

app.all('*', function(req, res, next) { 
// 如果想讓cookie發送至前端,必須設置為localhost:4000,而不是*
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Max-Age", 60); // 預檢請求的有效期
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods","PUT, POST, GET, DELETE, OPTIONS"); 
res.header("Content-Type", "application/json;charset=utf-8");
next(); 
});

關於上述代碼中res.header("Access-Control-Max-Age", 60); // 預檢請求的有效期,預檢請求可以參考這篇文章

參考文章

瀏覽器同源政策及其規避方法

Ajax不會自動帶上cookie

相關文章

webpack編譯優化

CSS垂直居中的12種實現方式

JavaScript萬物產生順序

談談JavaScript中的this機制