同domain(或ip),同端口,同协议视为同一个域,一个域内的脚本仅仅具备本域内的权限,能够理解为本域脚本只能读写本域内的资源,而没法访问其它域的资源。这种安全限制称为同源策略。
出于安全考虑,HTML的同源策略不容许JavaScript进行跨域操做, 直接发送跨域 AJAX 会获得以下错误:面试
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
随着Web App的功能愈来愈强 各类跨域的需求催生了无数的跨域手法。甚至在HTML5标准中都给出了官方的跨域方法, 也是最近应付面试的须要,拿一篇文章来总结既有的各类跨域手段。json
这些跨域通讯的方法大体能够分为两类:跨域
下面几个域名下的页面都是能够经过document.domain跨域互操做的: http://a.com/foo, http://b.a.com/bar, http://c.a.com/bar。 但只能以页面嵌套的方式来进行页面互操做,好比常见的iframe方式就能够完成页面嵌套:浏览器
// URL http://a.com/foo var ifr = document.createElement('iframe'); ifr.src = 'http://b.a.com/bar'; ifr.onload = function(){ var ifrdoc = ifr.contentDocument || ifr.contentWindow.document; ifrdoc.getElementsById("foo").innerHTML); }; ifr.style.display = 'none'; document.body.appendChild(ifr);
上述代码所在的URL是http://a.com/foo,它对http://b.a.com/bar的DOM访问要求后者将 document.domain往上设置一级:安全
// URL http://b.a.com/bar document.domain = 'a.com'
document.domain只能从子域设置到主域,往下设置以及往其余域名设置都是不容许的, 在Chrome中给出的错误是这样的:服务器
Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'baidu.com' is not a suffix of 'b.a.com'
<img>, <script>
在document.body中append一个具备src属性的HTML标签, src属性值指向的URL会以GET方法被访问,该访问是能够跨域的。app
其实样式表的
标签也是能够跨域的,只要是有src或href的HTML标签都有跨域的能力。
不一样的HTML标签发送HTTP请求的时机不一样,例如在更改src属性时就会发送请求,而script, iframe, link[rel=stylesheet]只有在添加到DOM树以后才会发送HTTP请求:cors
var img = new Image(); img.src = 'http://some/picture'; // 发送HTTP请求 var ifr = $('<iframe>', {src: 'http://b.a.com/bar'}); $('body').append(ifr); // 发送HTTP请求
<script>
是能够跨域的,并且在跨域脚本中能够直接回调当前脚本的函数。<script>
能够跨域的特性,跨域URL返回的脚本不只包含数据,还包含一个回调:// URL: http://b.a.com/foo var data = { foo: 'bar', bar: 'foo' }; callback(data);
该例子只用于示例,实际状况应当考虑名称隐藏等问题。
而后在咱们在主站http://a.com中,能够这样来跨域获取http://b.a.com的数据:dom
// URL: http://a.com/foo var callback = function(data){ // 处理跨域请求获得的数据 }; var script = $('<script>', {src: 'http://b.a.com/bar'}); $('body').append(script);
其实jQuery已经封装了JSONP的使用,咱们能够这样来:函数
$.getJSON( "http://b.a.com/bar?callback=callback", function( data ){ // 处理跨域请求获得的数据 });
$.getJSON与$.get的区别是前者会把responseText转换为JSON,并且当URL具备callback参数时, jQuery将会把它解释为一个JSONP请求,建立一个<script>
和全部依赖于建立HTML标签的方式同样,JSONP也不支持POST,而GET的数据是放在URL里的。通常来说URL限长是在2000字符左右。
在如何理解HTTP响应的状态码?一文中有更多关于HTTP响应状态码的讨论。
有些人注意到了IE6/7的一个漏洞:iframe之间的window.navigator对象是共享的。 咱们能够把它做为一个Messenger,经过它来传递信息。好比一个简单的委托:
// a.com navigation.onData(){ // 数据到达的处理函数 } typeof navigation.getData === 'function' || navigation.getData()
// b.com navigation.getData = function(){ $.get('/path/under/b.com') .success(function(data){ typeof navigation.onData === 'function' || navigation.onData(data) }); }
与document.navigator相似,window.name也是当前窗口全部页面所共享的。也能够用它来传递信息。 一样蛋疼的办法还有传递Hash(有些人叫锚点),这是由于每次浏览器打开一个URL时,URL后面的#xxx部分会保留下来,那么新的页面能够从这里得到上一个页面的数据。
前面提到的跨域手段都是某种意义上的Hack, HTML5标准中提出的跨域资源共享(Cross Origin Resource Share,CORS)才是正道。 它支持其余的HTTP方法如PUT, POST等,能够从本质上解决跨域问题。
例如,从http://a.com要访问http://b.com的数据,一般状况下Chrome会因跨域请求而报错:
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
错误缘由是被请求资源没有设置Access-Control-Allow-Origin,因此咱们在b.com的服务器中设置这个响应头字段便可:
Access-Control-Allow-Origin: * # 容许全部域名访问,或者 Access-Control-Allow-Origin: http://a.com # 只容许XXX域名访问
为 xhr 设置 withCredentials 后 CORS 方法跨域还可 携带Cookie,但 PUT/POST 请求须要注意处理 preflight 请求。
能够给任何一个window发送消息,不管是否同源。第二个参数能够是*但若是你设置了一个URL但不相符,那么该事件不会被分发。看一个普通的使用方式吧:
// URL: http://a.com/foo var win = window.open('http://b.com/bar'); win.postMessage('Hello, bar!', 'http://b.com');
// URL: http://b.com/bar window.addEventListener('message',function(event) { console.log(event.data); });
注意IE8及小于IE8的版本不支持addEventListener,须要使用attachEvent来监听事件。 参见:事件处理中的this:attachEvent, addEventListener, onclick
在HTML5以前,JSONP已经成为跨域的事实标准了,jQuery都给出了支持。 值得注意的是它只是Hack,并无产生额外的安全问题。 由于JSONP要成功获取数据,须要跨域资源所在服务器的配合,好比资源所在服务器须要自愿地回调一个合适的函数,因此服务器仍然有能力控制资源的跨域访问。
跨域的正道仍是要使用HTML5提供的CORS头字段以及window.postMessage, 能够支持POST, PUT等HTTP方法,从机制上解决跨域问题。 值得注意的是Access-Control-Allow-Origin头字段是资源所在服务器设置的, 访问控制的责任仍然是在提供资源的服务器一方,这和JSONP是同样的。