说到传统跨域方式,JSONP是最普遍为人所知的形式了。html
对于JS来讲,利用XMLHttpRequest没法请求非本域上的数据,可是却能够加载非本域的JS文件。
JSONP就是利用了这个所谓的“漏洞”。前端
试想当咱们在文档中插入一个script标签,请求一个 JS 文件时,该JS文件的内容是直接调用一个函数,
同时传入适当的数据。则对于方法内部,就已经能够经过参数的形式获取传入的数据。以下所示:ajax
此处的对象,就是传入的数据,而fun则是那个函数。该函数其实能够是任意函数名,只需在咱们的window域下定义,也能够根据需求定义在其余地方。问题在于传入的数据怎么来呢?其实这些对于前端来讲彻底不用管,只需让后端处理好后拼接成如上形式返回给咱们便可。而fun里面放什么,则取决于你要对返回的数据作什么样的处理了。json
另外,fun的函数名建议是随同跨域接口一块儿传输给server,以让server来进行拼接出合适的执行代码。完整代码以下:后端
也许你会遇到这样的情形:个人网页内嵌了一个iframe,该iframe所引用的页面跟我本域不同,此时我想修改iframe中的内容,该怎么办呢?跨域
或许你认为能够经过contentWindow获取iframe中的内容,但很惋惜的是咱们拿到这个contentWindow,却不能获取到里面的属性与方法(H5最新属性postMessage除外,后面会说起)。数组
这是由于浏览器的同源策略禁止了咱们这样的操做,由于它们分属不一样的源。浏览器
此时就须要用到document.domain了,也就是咱们只需将iframe中的document.domain设置成跟父页面相同的值便可。安全
好比当 http://loliner.baidu.com/inde... 内嵌了 http://baidu.com/example.html ,此时咱们只需将 index.html 的 document.domain 修改为 baidu.com便可实现与example.html通讯。服务器
可是问题来了,浏览器对document.domain的设置是有限制的,其只能设置成比自身域更高一级的域名,包括自身域。
好比存在http://b.a.com/index.html,则该页面 document.domain容许的值为
b.a.com a.com
而c.b.a.com则是不容许的。且两个页面若要通信,则二者的主域必须相同。
上例中,主域就为 baidu.com。
在一个窗口(window)的生命周期内,窗口载入的全部的页面都是共享一个window.name,每一个页面对window.name都有读写的权限。
以上是对window.name的定义。试想若是有两个不一样域的页面顺次被同一个窗口加载,那么对于这两个页面来讲,二者就能共享window.name,从而实现通讯。
如何顺次加载?利用window.location改变窗口的地址就能够办到这一点。
好比,http://example.com/a.html 彻底加载后,修改window.name为指定值。而后经过window.location将窗口定向到 http://loliner.com/b.html,此时b.html中window.name的值就为上一个页面中设置的值。
window.name的值只能是一个字符串,最大2M,但对咱们来讲已经够用了。若是你要传递对象或数组,进行JSON格式化便可。
可是,虽然这样实现了数据通讯,可是咱们不能来回这样切来切去,那怎么办呢?这时候就能够利用 iframe + contentWindow 了。
咱们能够在 b.html 页面添加一个隐藏的iframe,该iframe指向a.html,当a.html彻底加载并修改了window.name以后,咱们将iframe从新指向与b.html在同一个域名下的c.html,此时就能够经过 iframe 的 contentWindow 获取到 name 的值了。
完整代码以下:
咱们知道在同一个页面嵌入多个iframe,每一个iframe的域都不同时,虽然咱们能够获取iframe的window对象,却不能访问其中的属性和方法。但H5的最新属性postMessage除外,那么咱们是否能够在此利用这样的一个“漏洞”来进行跨域呢?
答案是能够的。
咱们只需在iframe中设置onmessage监听,而后在父级页面中获取iframe的window对象,将父页面所要传递的信息经过window.postMessage发出便可。
完整代码以下:
首先要让iframe监听onmessage事件。
接下来则是在父页面中获取iframe的window对象并调用postMessage传递信息。
注意,此处的postMessage所发送的消息在低版本浏览器下只能为字符串,若要传递对象或数组,只能JSON格式化。
另外,第二个参数指的是域,表示你要将信息发送到哪一个域,参数格式为协议+主机+端口,其实跟上面所说document.domain的值是一回事。
在全部以上提到的跨域方法中,不得不说都是利用了大大小小的漏洞来绕过同源策略。而不管如何进行绕过,有一点能够确定的是,WEB开发当中确实须要跨域获取资源。
因此,W3C为咱们制订了 CORS 跨域访问机制。CORS对于开发者来讲最大的好处就是,无需考虑以什么样的方式绕过同源策略请求跨域资源,直接使用ajax便可。
那么谁去负责跨域的安全性呢?答案是由Server进行控制。
Server在接收到请求的时候,需判断请求来源,若是该来源合法,返回正常数据,同时须要在请求返回头response header中添加必要 CORS 字段。
对于浏览器来讲,若该请求的返回头信息中,包含该 CORS 字段,那么浏览器就会正常读取返回的信息,不然,就会抛出No 'Access-Control-Allow-Origin' header is present on the requested resource.禁止跨域异常。
上面所说到的CORS字段,分为必须与可选:
Access-Control-Allow-Origin(必须)
该服务器所容许跨域的源,若是为 * ,则表示容许全部源对该服务器进行请求。
发送请求的一方的域,必须包含在该值所指定的源内。
Access-Control-Allow-Credentials(可选)
表示是否容许发送cookie,该值为一个布尔值,默认为false。
Access-Control-Expose-Headers(可选)
获取response header中其余字段的值。
因此,简单的说,CORS 模式中的跨域请求其实与前端开发者已经没有太大的关系,只要Server端控制好接口数据,并按需写入CORS字段便可。
另外,CORS跨域请求能够分为简单请求与复杂请求。
对于简单请求,浏览器直接发送CORS请求便可,跟上面所说流程一致。
HEAD、GET、POST都属于简单请求。
对于复杂请求,则须要首先发出预检请求,判断是否能够跨域,而后再发送真实请求。
PUT和DELETE,或Content-Type字段类型为application/json的都属于复杂请求。
就目前来讲,JSONP 及 CORS 算是最经常使用的跨域形式了,也能知足咱们的绝大部分需求。
相对于JSONP,CORS W3C规定的标准跨域方式,功能更强大,安全性也更好。
JSONP只支持GET请求,而 CORS 支持多种请求。
JSONP 没有域的概念,CORS 则有精确的指定哪些域能够获取资源。
CORS 将域的安全性管理彻底交给Server管理,更加安全。
Flash跨域也有多种形式,此处只介绍最经常使用的 crossdomain.xml 策略文件 模式及allowDomain 权限授予。
corssdomain.xml 跨域策略文件
与 CORS 复杂请求同样,Flash在进行跨域请求时,默认首先会发送预检请求,判断本域是否合法。该预检请求会去跨域请求的根目录获取 corssdomain.xml 文件,该文件包含了跨域策略信息。好比最重要的一点,就是告诉Flash哪些域能够跨域请求该服务器。
举个栗子,http://baidu.com/a.html下有某swf文件要向http://loliner.com/请求资源。此时会通过以下步骤:
Flash发现请求须要跨域,则首先请求http://loliner.com/crossdomai...文件;
若crossdomain.xml文件能正常返回,则分析其中的策略,查看本域是否合法;
若本域合法,则发送跨域请求;若不合法,则直接封杀请求。
此处注意,若是本域非法,Flash就会直接封杀了请求,那么咱们在浏览器控制台是捕获不到该请求的,由于就没发出去。
那么Flash是怎么判断本域是否合法的呢?咱们先来看一下一个最简单的corssdomain.xml文件结构。
全部的 crossdomain.xml 都以 <corss-domian-policy> 做为根节点,里面的每个<allow-access-from>都为一个策略。
其中,键值 domain 表示容许跨域的源,此处就跟 CORS 的 Access-Control-Allow-Origin 字段同样了。
而 secure ,则表示请求是否以加密进行传输。若是 corssdomain.xml 文件是从 https 协议下加载的,那么 secure 默认为 true。此时Flash若发现本域为非 https 协议,纵使 domain 合法,也会封杀请求。若是secure为false,则代表Flash能够发送非https加密过的请求。
若是说介绍 crossdomain.xml 文件时咱们是以 Client 端为角度的,那么介绍 Security.allowDomain 则必须以 Server 做为角度。
你可能会遇到以下情形,开发了多个swf,它们之间相互依赖,但以不一样的域发布到网上。此时你只但愿某些特定的域才有权限去修改或调用该swf中的属性及方法。那么此时,你就须要用到allowDomain方法对你所容许的域进行受权了。
如下是 allowDomain 的描述:
容许指定的域中的 SWF 文件访问调用了此方法的 SWF 文件中的对象和变量。
再举个栗子,http://a.com/a.swf 须要调用及修改 http://b.com/b.swf 中的属性及方法。
那么,就必须在b.swf中调用 Security.allowDomain('a.com')以容许a.com域对本身进行访问。
另外,要注意很是重要的一点,Security.allowDomain跨域权限授予是不对称的。
也就是说,在上面的栗子中,只容许a.swf访问b.swf,但b.swf却不可以访问a.swf。
若是非要这样作,那么就在a.swf中对b.com进行权限授予。
其次,对于HTTPS来讲,即便调用了该方法,若试图利用在HTTP下的swf去修改HTTPS下的swf,此时也是非法的。由于这自己就不安全。
若是非要这样作,可让HTTPS下的swf调用Security.allowInsecureDomain()对HTTP域进行权限授予
反观 Flash 跨域与H5 CORS 跨域,虽然二者在形式上有所不一样,但本质上都向着如下两个特色靠拢:
先询问,后请求; 安全性靠Server端来维护。
对于 CORS 简单请求来讲,浏览器是不会进行预检请求发送的,这样一个请求搞定全部的事,却不必定彻底合理。由于对于浏览器来讲,我在没有知道Server是否定为个人请求合法前,就已经抛出了大量的查询数据。这就有可能形成数据传输的冗余。
另外,若是因为开发过程不当,频繁发送不合法的跨域请求,自己就是一个资源的浪费。
因此,才须要到复杂请求。
而对于 Flash crossdomain.xml 这种方式(与 CORS 复杂请求相似),就避免了频繁发送不合法的跨域请求,由于在crossdomain.xml返回以前,Flash是不会发送任何跨域请求,直接在本地就将其封杀。同时crossdomain.xml并不是每次跨域前都会请求,其有一个有效期。
但若我自己只需请求一次数据,但Server因为业务繁重却给我返回一个庞大的crossdomain.xml,这反而又显得不合理了。