跨域的解决方案
平时我们在进行web开发时,总会遇到一些数据的传递,其中最常见的当属前后端数据的传递。当然,除了前后端数据传递,还有一些其他的场景,比如:
- 页面和其新打开的窗口之间的数据传递
- 多窗口之间的数据传递
- 页面与内嵌的iframe数据传递
这些都涉及到跨域。那么什么是跨域?
什么是跨域
在HTML中,<a>, <form>, <img>, <script>, <iframe>, <link>等标签以及ajax都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样,那么请求就会失败。这里的域指的是:若是协议+域名+端口号均相同,那么就表明是同域。跨域最根本的原因还是浏览器的同源策略。
下表给出了https://www.shchome.top 同源示例:
| URL | 结果 | 原因 |
|---|---|---|
| http://www.shchome.top | 失败 | 协议不同 |
| https://www.shcblog.com | 失败 | 域名不同 |
| https://www.shchome.top:8080 | 失败 | 端口不同 |
| https://www.shchome.top/page/2/ | 成功 | 协议,域名,端口均相同 |
那么浏览器为什么要使用同源策略这种机制呢?
浏览器引入同源策略主要是出于安全考虑,防御CSRF攻击。CSRF主要是利用用户的登录状态发起恶意请求。
试想一下,如果没有同源策略,那么我们每次发起HTTP请求都会带上请求地址的cookie,此时黑客就可以做如下攻击:
- 用户登录了自己的网上银行页面 mybank.com, mybank.com向用户的cookie中添加用户的标识。
- 用户浏览了恶意页面 virulence.com,执行了页面中恶意的JS代码。
- virulence.com向mybank.com发起恶意的HTTP请求,请求会默认把mybank.com对应的cookie也同时发送过去。
- mybank.com服务端从发送的cookie中提取用户标识,验证用户无误,response中返回请求的数据,此时数据就泄露了。
iframe之间的跨域也是类似,如果iframe之间可以跨域访问,那么可以这样攻击:
- 仿造一个假网站,里面用iframe嵌套 mybank.com 银行网站。
- 设置iframe样式,使得网站和银行的网站之间除了域名之外没有任何差别。
- 这时如果用户输入用户名密码,这样主网站就可以通过跨域访问mybank.com整个DOM节点,这样就可以拿到用户的输入。
所以说有了跨域限制之后,我们就能更安全的上网了。当然,跨域并不能完全的防止CSRF攻击。
现在我们再来考虑一个问题,请求跨域了,那么请求到底有没有发出去?
请求必然是发出去了,但是在请求响应时,被浏览器拦截了。跨域是为了阻止用户读取到其他域名下的内容,ajax可以获取响应,但是浏览器认为这是不安全的,所以拦截了响应。这也可以说明跨域并不能防止CSRF攻击,因为请求毕竟是发出去了。
今天我们先来说说解决跨域的一种方法–postMessage。
什么是postMessage
postMessage 是HTML5引入的一个新的API,postMessage方法允许来自不同源的脚本采用异步方式进行通信。可以实现跨文本文档,多窗口,跨域消息传递,多用于窗口间数据通信,这也使它成为跨域通信的一种有效解决方案。
postMessage的使用
发送消息
1 | otherWindow.postMessage(message, targetOrigin, [transfer]); |
otherWindow
窗口的一个引用,比如iframe的contentWindow属性,执行window.open返回的窗口对象,或者是命名过的或数值索引的window.frames。
message
需要发送到其他窗口的数据,注意:message在发送时会被序列化。
targetOrigin
通过窗口的origin属性来指定哪些窗口能够接收到消息事件,指定后只有对应origin的窗口才能接收到消息。设置通配符‘*’表示可以发送到任意的窗口,但是出于安全性最好不要这么做,如果想要发送到和当前窗口同源的窗口,只需要设置为‘/’即可。
transfer | 可选属性
是一串和message同时传递的Transferable对象,这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
接收消息
目标窗口是通过执行方法监听函数来接收发送过来的消息
1 | window.addEventListener("message", receiveMessage, false); |
event对象有三个属性,分别是origin,data和source。event.data表示接收的消息,event.origin表示发送message的消息源,包括源的协议、url、端口;event.source表示发送消息的窗口的引用。通过这个引用我们可以建立两个源之间的通信。
postMessage解决跨域问题
postMessage解决跨域非常简单,在源消息页面只需要使用如下代码
1 | window.parent.postMessage('isClosed', 'https://shchome.top') |
在接收消息的页面中
1 | window.addEventListener('message', msgHandler, false) |
本文简单的介绍了跨域和解决跨域方法的一种,跨域作为前端一个比较重要的知识点,掌握跨域还是很有必要的。而postMessage作为HTML5中一个新的API,能够很好的解决跨域问题,特别是不同窗口之间的通信,因此了解它也是有必要的。