DOM跨域

同源策略限制从一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。html

可是有时候跨域请求资源是合理的需求,本文尝试从多篇文章中汇总至今存在的全部跨域请求解决方案。git

跨域请求github

首先须要了解的是同源和跨源的概念。对于相同源,其定义为:若是协议、端口(若是指定了一个)和主机对于两个页面是相同的,则两个页面具备相同的源。只要三者之一任意一点有不一样,那么就为不一样源。后端

当一个资源从与该资源自己所在的服务器的域或端口不一样的域或不一样的端口请求一个资源时,资源会发起一个跨域 HTTP 请求。跨域

而有关跨域请求受到限制的缘由能够参考以下 MDN 文档片断:浏览器

跨域不必定是浏览器限制了发起跨站请求,而也多是跨站请求能够正常发起,可是返回结果被浏览器拦截了。安全

最好的例子是 CSRF 跨站攻击原理,请求是发送到了后端服务器不管是否跨域!注意:有些浏览器不容许从 HTTPS 的域跨域访问 HTTP,好比 Chrome 和 Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。bash

解决方法汇总服务器

如下咱们由简及深介绍各类存在的跨域请求解决方案,包括 document.domain, location.hash, window.name, window.postMessage, JSONP, WebSocket, CORS 。dom

document.domain

document.domain 的做用是用来获取/设置当前文档的原始域部分,例如:

// 对于文档 www.example.xxx/good.html 
document.domain=”www.example.xxx”

// 对于URI http://developer.mozilla.org/en/docs/DOM 
document.domain=”developer.mozilla.org” 
若是当前文档的域没法识别,那么 domain 属性会返回 null。

在根域范围内,Mozilla容许你把domain属性的值设置为它的上一级域。例如,在 developer.mozilla.org 域内,能够把domain设置为 “mozilla.org” 但不能设置为 “mozilla.com” 或者”org”。

所以,若两个源所用协议、端口一致,主域相同而二级域名不一样的话,能够借鉴该方法解决跨域请求。

好比若咱们在http://a.github.io 页面执行如下语句:

document.domain = “github.io” 
那么以后页面对 github.io 发起请求时页面则会成功经过对 github.io 的同源检测。

比较直接的一个操做是,当咱们在 a.github.io 页面中利用 iframe 去加载 github.io 时,经过如上的赋值后,咱们能够在 a.github.io 页面中去操做 iframe 里的内容。

咱们同时考虑另外一种状况:存在两个子域名 a.github.io 以及 b.github.io , 其中前者域名下网页 a.html 经过 iframe 引入了后者域名下的 b.html,此时在 a.html 中是没法直接操做 b.html 的内容的。

一样利用 document.domain ,咱们在两个页面中均加入

document.domain=’github.io’ 这样在以上的 a.html 中就能够操做经过 iframe 引入的 b.html 了。

document.domain的优势在于解决了主语相同的跨域请求,可是其缺点也是很明显的:好比一个站点受到攻击后,另外一个站点会所以引发安全漏洞;若一个页面中引入多个 iframe,想要操做全部的 iframe 则须要设置相同的 domain。

location.hash

location.hash 是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。例如:

// 对于页面 http://example.com:1234/test.htm#part2 
location.hash = “#part2” 
同时,因为咱们知道改变 hash 并不会致使页面刷新,因此能够利用 hash 在不一样源间传递数据。

假设 github.io 域名下 a.html 和 shaonian.eu 域名下 b.html 存在跨域请求,那么利用 location.hash 的一个解决方案以下:

a.html 页面中建立一个隐藏的 iframe, src 指向 b.html,其中 src 中能够经过 hash 传入参数给 b.html 。

b.html 页面在处理完传入的 hash 后经过修改 a.html 的 hash 值达到将数据传送给 a.html 的目的。

a.html 页面添加一个定时器,每隔必定时间判断自身的 location.hash 是否变化,以此响应处理 。

以上步骤中须要注意第二点:如何在 iframe 页面中修改 父亲页面的 hash 值。因为在 IE 和 Chrome 下,两个不一样域的页面是不容许 parent.location.hash 这样赋值的,因此对于这种状况,咱们须要在父亲页面域名下添加另外一个页面来实现跨域请求,具体以下:

假设 a.html 中 iframe 引入了 b.html, 数据须要在这两个页面之间传递,且 c.html 是一个与 a.html 同源的页面 
a.html 经过 iframe 将数据经过 hash 传给 b.html 
b.html 经过 iframe 将数据经过 hash 传给 c.html 
c.html 经过 parent.parent.location.hash 设置 a.html 的 hash 达到传递数据的目的 
location.bash方法的优势在于能够解决域名彻底不一样的跨域请求,而且能够实现双向通信;而缺点则包括如下几点:

利用这种方法传递的数据量受到 url 大小的限制,传递数据类型有限 
因为数据直接暴露在 url 中则存在安全问题 若浏览器不支持 onhashchange 事件,则须要经过轮训来获知 url 的变化 有些浏览器会在 hash 变化时产生历史记录,所以可能影响用户体验 window.name。

该属性用于获取/设置窗口的名称。其特征在于:一个窗口的生命周期内,窗口载入的全部页面共享该值,且都具备对该属性的读写权限。

这意味着若是不修改该值,那么在不一样页面加载以后该值也不会变,且其支持长达 2MB 的存储量。

利用该特性咱们能够将跨域请求用以下步骤解决:

在 a.github.io/a.html 中建立 iframe 指向 b.github.io/b.html (页面会将自身的 window.name 附在 iframe 上) 

给 a.github.io/a.html 添加监听 iframe 的 onload 事件,在该事件中将 iframe 的 src 设置为本地域的代理文件(代理文件和a.html处于同一域下,能够相互通讯),同时能够传出 iframe 的 name 值 获取数据后销毁 iframe,释放内存,同时也保证了安全 window.name的优点在于巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操做。

window.postMessage

HTML5 为了解决这个问题,引入了一个全新的 API:跨文档通讯 API(Cross-document messaging)。这个 API 为 window 对象新增了一个 window.postMessage 方法,容许跨窗口通讯,不论这两个窗口是否同源。

API 的详细使用方法请见 MDN 。

相关文章
相关标签/搜索