>>>点击获取更多文章<<<javascript
那到底什么是跨域,简单地理解就是由于JavaScript同源策略的限制,a.com 域名下的js没法操做b.com或是c.a.com域名下的对象。更详细的说明能够看下表:php
特别注意两点:html
“URL的首部”指window.location.protocol +window.location.host,也能够理解为“Domains, protocols and ports must match”(本段来自网络,我的以为这段对js跨域描述得在清晰不过了)。java
跨域请求无处不在,下面来看看咱们都是如何处理跨域请求的:jquery
虽然浏览器默认禁止了跨域访问,但并不由止在页面中引用其余域的JS文件,script标签的src属性引用指向接收方的一个处理地址(后台),该地址返回的javascript方法会被执行,另外URL中能够传入一些参数,该方法只支持GET方式提交参数。咱们经常使用FloadJS方法用的就是这种跨域方式。web
其中jquery的getScript 方法 就是相似那样的方法(经过 GET 方式请求载入并执行一个 JavaScript 文件, 至关于经过src的形式的导入一个外部的js)。ajax
语法:jQuery.getScript(url,success(response,status)),该函数是简写的 Ajax 函数,等价于:算法
$.ajax({ Type: get, url: url, dataType: "script", success: success });
JSONP是服务器与客户端跨源通讯的经常使用方法。最大特色就是简单适用,老式浏览器所有支持,服务器改造很是小。json
它的基本思想是,网页经过添加一个script元素,向服务器请求JSON数据,这种作法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。跨域
首先,网页动态插入script元素,由它向跨源网址发出请求。
function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag('http://example.com/ip?callback=foo'); } function foo(data) { console.log('Your public IP address is: ' + data.ip); };
上面代码经过动态添加script元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。
服务器收到这个请求之后,会将数据放在回调函数的参数位置返回。
foo({ "ip": "8.8.8.8" });
因为script元素请求的脚本,直接做为代码运行。这时,只要浏览器定义了foo函数,该函数就会当即调用。做为参数的JSON数据被视为JavaScript对象,而不是字符串,所以避免了使用JSON.parse的步骤。
其中 jQuery.getJSON(url,data,success(data,status,xhr)) 都是利用jsonp的道理 ,该函数是简写的 Ajax 函数,等价于:
$.ajax({ url: url, data: data, success: callback, dataType: json });
另外,jsonp目前只支持get请求方式,对post请求不支持。JSONP的优点在于支持老式浏览器,以及能够向不支持CORS的网站请求数据。
举例来讲,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就能够共享Cookie。
document.domain = 'example.com';
如今,A网页经过脚本设置一个 Cookie。
document.cookie = "test1=hello";
B网页就能够读到这个 Cookie。
var allCookie = document.cookie;
若是两个网页不一样源,就没法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口没法通讯。好比,父窗口运行下面的命令,若是iframe窗口不是同源,就会报错。
document.getElementById("myIFrame").contentWindow.document // Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.
上面命令中,父窗口想获取子窗口的DOM,由于跨源致使报错。反之亦然,子窗口获取主窗口的DOM也会报错。
window.parent.document.body // 报错
若是两个窗口一级域名相同,只是二级域名不一样,那么设置上一节介绍的document.domain属性,就能够规避同源政策,拿到DOM。
otherWindow.postMessage(message, targetOrigin, [transfer]);
__otherWindow__,其余窗口的一个引用,好比iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。好比document.getElementsByTagName('iframe')[0].contentWindow; window.open('http://bbb.com', 'iframeName'); window.frames[0];
__message__,将要发送到其余 window的数据。它将会被结构化克隆算法序列化。这意味着你能够不受什么限制的将数据对象安全的传送给目标窗口而无需本身序列化。
__targetOrigin__,经过窗口的origin属性来指定哪些窗口能接收到消息事件,其值能够是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,若是目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者彻底匹配,消息才会被发送。
__transfer__,是一串和message 同时传递的 Transferable 对象. 这些对象的全部权将被转移给消息的接收方,而发送一方将再也不保有全部权。
父窗口和子窗口均可以经过message事件,监听对方的消息。message事件的事件对象event,提供如下三个属性。
代码以下:
window.addEventListener('message', receiveMessage); function receiveMessage(event) { if (event.origin !== 'http://aaa.com') return; if (event.data === 'Hello World') { event.source.postMessage('Hello', event.origin); } else { console.log(event.data); } }
其中,本身作了一个例子,父窗口按期发送随机消息给子窗口,子窗口接收随机信息,再反馈给父窗口进行跨域通讯,详情效果请点击观看。具体代码以下:
//http://www.zhangbing.club/images/file/postmessage.html页面代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="parentdiv"></div> <iframe name="childframe" id="childframe" src="http://js.zhangbing.name/demoa12.html" frameborder="1" width="700px" height="300px"></iframe> <script type="text/javascript"> window.onload=function(){ setInterval(function(){ window.frames[0].postMessage('我是来自dotClub域名的消息; | 随机数:'+Math.random(),'http://js.zhangbing.name'); },1000) } window.addEventListener('message', function(e) { document.getElementById('parentdiv').innerHTML = e.data; },false); </script> </body> </html> //http://js.zhangbing.name/demoa12.html页面代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>child</title> </head> <body> <div id="childdiv"></div> <script type="text/javascript"> function receiveMessage(event) { // 咱们能信任信息来源吗? console.log(event.origin); if (event.origin !== "http://www.zhangbing.club") return; document.getElementById('childdiv').innerHTML = event.data; // 假设你已经验证了所受到信息的origin (任什么时候候你都应该这样作), 一个很方便的方式就是把enent.source // 做为回信的对象,而且把event.origin做为targetOrigin var a = event.data.split("|"); event.source.postMessage("我是来自dotName域名的消息 | 接收的随机数是:"+a[1]+"| 如今反馈给你",event.origin); } window.addEventListener("message", receiveMessage, false); </script> </body> </html>
WebSocket是一种通讯协议,使用ws://(非加密)和wss://(加密)做为协议前缀。该协议不实行同源政策,只要服务器支持,就能够经过它进行跨源通讯。
下面是一个例子,浏览器发出的WebSocket请求的头信息(摘自维基百科)。
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪一个域名。
正是由于有了Origin这个字段,因此WebSocket才没有实行同源政策。由于服务器能够根据这个字段,判断是否许可本次通讯。若是该域名在白名单内,服务器就会作出以下回应。
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
基本原理就是,由客户端将请求发给同域服务器,再由同域服务器的代理来请求数据并将响应返回给客户端。
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。__它容许浏览器向跨源服务器,发出XMLHttpRequest(Level2)请求,从而克服了XMLHttpRequest老版本只能向同一域名的服务器请求数据__。
CORS须要浏览器和服务器同时支持。目前,全部浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。所以,__实现CORS通讯的关键是服务器__。只要服务器实现了CORS接口,就能够跨源通讯。
下面是具体php例子的服务器支持代码:
// 指定容许其余域名访问 $origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : ''; $allow_origin = array( 'http://localhost:1234', 'http://cms.cjwsc.com' ); if(in_array($origin, $allow_origin)){ header('Access-Control-Allow-Origin:'.$origin); } // 响应类型 header("Access-Control-Allow-Methods: GET, POST, DELETE"); // 容许跨域请求带cookie header("Access-Control-Allow-Credentials: true"); // 响应头设置 header("Access-Control-Allow-Headers: Content-Type, X-Requested-With, Cache-Control,Authorization");
CORS能够作不少事情和选择,能够支持全部类型的HTTP请求,和是否带上cookie等!
更多详细理解CORS原理,请移步到个人文章CORS跨域!