在讲跨域前必须了解同源策略:同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当一个百度浏览器执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。简单的一句话就是:“如果两个页面拥有相同的协议(protocol),端口(如果指定),和主机,那么这两个页面就属于同一个源(origin)。”下图为是否同源:
为了保证用户信息的安全,防止恶意的网站窃取数据。同源策略下:Cookie、LocalStorage 和 IndexDB 无法读取,DOM 无法获得,AJAX 请求不能发送。(ps:如果想更多了解同源策略可以看阮一峰大神的博客:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html)。
为了解决不同源之间通讯,跨域因此而生:
方法一:JSONP
jsonp:JSONP是json with padding的简称,jsonp看起来跟json差不多,只不过是包含在函数中的json数据而已。
callback({"name":"xiaotutu"});
因为script标签是不受同源策略影响的,它可以引入来自任何地方的js文件。而jsonp的原理就是,在客户端和服务端定义一个函数,当客户端发起一个请求时,服务端返回一段javascript代码,其中调用了在客户端定义的函数,并将相应的数据作为参数传入该函数。一般情况下,为了这个script标签能够动态的调用,我们可以通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务了。代码如下:
function jsonp_callback(data) { console.log(data); } function jsonpAjax(){ var url = "http://a.com/b.php?jsonp_callback=jsonp_callback"; var script = document.createElement(‘script‘); // 发送请求 script.src = url; document.head.appendChild(script); } jsonpAjax()
当服务器接受到callback时候会返回相应的代码
jsonp_callback({ "name": "chentutu" });
jsonp是想对来说比较简单的,灵活的利用了<script>的不受限制从其它域加载资源。因为javascript是有效代码所以在请求完成后,就会被立即执行。
方法二:<img>
跟script一样能不受其它域限制加载资源,使用它们的onload和onerror事件处理程序来确定是否响应。但它只能用来发送GET请求,且无法获取服务端的响应文本,所以只能利用它实现一些简单的、单向的跨域通信,例如跟踪用户的点击。大家看看下面的例子:
var img = new Image(); img.onload = function(){ console.log(‘done‘) img.onload = null; img = null; } img.src = "http://xx/xx.gif"
方法三:CORS
CORS(Cross-Origin Resource Sharing,跨源资源共享)是W3C小组制定的一个标准,它允许浏览器实现跨域通信。通过建立一个XmlHttpRequest对象,CORS允许开发者象实现同源请求那样实现跨源请求。CORS(跨源资源共享)的实现目标非常简单。假设站点B想访问站点A中的某些数据,使用同源请求将不能满足这一需求。但是,通过CORS请求,站点A可以通过在部分页面中加入特殊的响应头的方法来允许站点B访问。
注意CORS只支持以下浏览器:
- Chrome 3以上版本的浏览器
- Firefox 3.5以上版本的浏览器
- Safari 4以上版本的浏览器
- Internet Explorer 8以上版本的浏览器
下面就是一个创建请求对象的代码:
function createCORSRequest(method, url) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { //检查XMLHttpRequest对象是否拥有一个withCredentials属性 //withCredentials属性只属于XMLHTTPRequest2对象 xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined") { //否则,检查XDomainRequest对象 Otherwise, check if XDomainRequest. //XDomainRequest对象只存在于IE浏览器中,且被IE浏览器用来创建CORS请求 xhr = new XDomainRequest(); xhr.open(method, url); } else { //否则,CORS不被当前浏览器支持 xhr = null; } return xhr; } var xhr = createCORSRequest(‘GET‘, url); if (!xhr) { throw new Error(‘CORS不被支持!‘); }
在默认情况下,标准的CORS请求不发送或设置任何cookie。为了在请求中包含cookie,你需要将XmlHttpRequest对象的withCredentials属性值设为true。为了达到目的,服务器端必须将Access-Control-Allow-Credentials响应头设为true。.withCredentials属性值的设置将会使跨源请求中可以包含任何cookie,开发者也可以通过使用该属性值来跨源设置任何cookie。请注意,这些cookie也可以使用同源安全策略,即被跨源设置的cookie中的内容不能被同源的JavaScript脚本代码所访问,只能被设置这些cookie的源所访问。
一个使用CORS请求的完整代码示例如下所示:
//创建XHR对象 function createCORSRequest(method, url) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { //Chrome/Safari/Firefox浏览器中的XHR对象 xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined") { //在IE浏览器中创建XDomainRequest对象 xhr = new XDomainRequest(); xhr.open(method, url); } else { //CORS不被支持. xhr = null; } return xhr; } //从服务器端响应中获取title标签 function getTitle(text) { return text.match(‘‘)[1]; } //创建CORS请求 function makeCorsRequest() { // bibliographica.org支持CORS请求 var url = ‘http://bibliographica.org/‘; var xhr = createCORSRequest(‘GET‘, url); if (!xhr) { alert(‘您的浏览器不支持跨源请求‘); return; } //处理服务器端响应 xhr.onload = function() { var text = xhr.responseText; var title = getTitle(text); alert(‘来自‘ + url + ‘的响应: ‘ + title); }; xhr.onerror = function() { alert(‘跨源请求失败!‘); }; xhr.send(); }
CORS分为简单和非简单模式:详细请见阮一峰大神的博客:http://www.ruanyifeng.com/blog/2016/04/cors.html。
方法四:document.domain
- 原理:相同主域名不同子域名下的页面,可以设置
document.domain
让它们同域 - 限制:同域document提供的是页面间的互操作,需要载入iframe页面
以下是document.domain实际操作的代码:
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);
注意:如果修改了document.domain,则在某些机器上的IE678里,获取location.href有权限异常。document.domain
只能从子域设置到主域,往下设置以及往其他域名设置都是不允许的。
方法五:window.postMessage
- 原理:HTML5允许窗口之间发送消息
- 限制:浏览器需要支持HTML5,获取窗口句柄后才能相互通信
//发送端 var win = window.open(‘http://b.com/bar‘); win.postMessage(‘Hello world!‘, ‘http://b.com‘); //接收端 window.addEventListener(‘message‘,function(event) { console.log(event.data); });
想了解window.postMessage的博友可以看看张鑫旭老师的博客:http://www.zhangxinxu.com/wordpress/2012/02/html5-web-messaging-cross-document-messaging-channel-messaging/。
方法六:WebSocket
WebSocket 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很棒的实现。(不用http协议有自己的一套协议)。
在客户端的实例:
var ws = new WebSocket(‘ws://127.0.0.1:8080/url‘); //新建一个WebSocket对象,注意服务器端的协议必须为“ws://”或“wss://”,其中ws开头是普通的websocket连接,wss是安全的websocket连接,类似于https。 ws.onopen = function() { // 连接被打开时调用 }; ws.onerror = function(e) { // 在出现错误时调用,例如在连接断掉时 }; ws.onclose = function() { // 在连接被关闭时调用 }; ws.onmessage = function(msg) { // 在服务器端向客户端发送消息时调用 // msg.data包含了消息 }; // 这里是如何给服务器端发送一些数据 ws.send(‘some data‘); // 关闭套接口 ws.close();
详情大家还是看红宝书591吧。