跨域的限制
- 无法获取
cookie
、localStorage
、indexDB
- 无法获得
DOM
- 无法发送
Ajax
关于cookie
共享cookie
cookie
,是服务器端写入浏览器端的小段信息,只有同源的网页才能共享cookie
,一级域名相同但二级域名不同的网页只要设置相同的document.domain
,就可以共享cookie
补充知识点:
- 一级域名
qq.com
- 二级域名
game.qq.com
、www.qq.com
、lol.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.com
、w2.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
,但依旧无法获取上述设置的
cookie
:
gao=peng
此时我们看下cookie存在的表现:
我们发现在w1.gaopeng.com
设置的
cookie
的
Domain=w1.gaopeng.com
发现
w2.gaopeng.com
依然没有上述设置的
cookie
突然想了一下如果将cookie设置为二级域名会怎么样呢? 在w1.gaopeng.com
下,设置cookie
w2.gaopeng.com
下,会获取到刚刚设置的
cookie
再次观察w1.gaopeng.com
、w2.gaopeng.com
的cookie
因此得出了结论,cookie
的共享与网站的是否同源并无明显的关系,能否共享应该看cookie
自身的domain
,如果domain
相同,就可以共享
猜想:存在两个站点不同源(协议、端口不同)
A站:https://www.example.com:3443/
B站:http://www.example.com:3445/
由于cookie
的domain
都是www.example.com
,那么这两个页面的cookie
是可以共享的
关于获取DOM
iframe
和window.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;
}
}
复制代码
在这里由于iframe
与window.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
主要谈论一下CORS
对cookie
的影响
ajax
会自动带上同源的cookie
,不会带上不同源的cookie
- 可以通过前端设置
withCredentials
为true
, 后端设置Header
的方式来让ajax
自动带上不同源的cookie
,但是这个属性对同源请求没有任何影响 - 如果
ajax
请求设置withCredentials
为true
,注意: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); // 预检请求的有效期
,预检请求可以参考这篇文章