同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
什么是源?
如果协议,端口(如果指定了一个)和主机对于两个页面是相同的,则两个页面具有相同的源。
如下:http://store.company.com:8080/dir/page.html
协议为:http:
端口为:8080
主机为:store.company.com;
当我们想对不同源的资源之间进行交互,服务器会报如下的错误:
Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.
但是在实际的开发中,经常会遇到需要跨域进行资源交互。
接下来,简要概述几种可以跨域的方法:
第一种,利用window.domian.
假设对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况。
www.a.com上的a.html
document.domain = 'a.com'; var ifr = document.createElement('iframe'); ifr.src = 'http://script.a.com/b.html'; ifr.style.display = 'none'; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; // 在这里操纵b.html alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue); };
script.a.com上的b.html
document.domain = 'a.com';
总的来说,这种方法并不是最好的,因为它的适应情况非常狭窄,只有在主域相同而子域不同才可以使用,并且额外的创建iframe和html等元素,以及设置document.domain。
第二种:JSONP
JSONP和JSON是完全不同的概念,该方法也是最著名的跨域方法。
我们知道,script标签可以获得任何地址的内容,返回的内容都是以javascript的形式运行。
可以通过动态的创建一个script标签来实现跨域。
发起请求页面的代码:
<script type="text/javascript"> var MyCallBack = function(data){ console.log(data) }; // 提供jsonp服务的url地址 var url = "localhost:8080/test2/index.jsp?parme=123&callback=MyCallback"; // 创建script标签,设置其属性 var script = document.createElement('script'); script.setAttribute('src', url); // 把script标签加入head,此时调用开始 document.getElementsByTagName('head')[0].appendChild(script); </script>
localhost:8080/test2/index.jsp下的代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% response.setContentType("text/html,charset=utf-8"); String callback=request.getParameter("callback"); //获取到数据返回时调用的函数名 String parme = request.getParameter("parme"); response.getWriter().print(callback+"([ { name:"跨域访问成功!"},{ name:"跨域访问失败!"}])"); //直接用print的方式输出javascript调用函数并传值。这样在调用方的javascript代码中就相当于调用了此函数。 %>
第三种:利用iframe和location.hash
如第一种方法中,我们在想要发起请求的页面A上添加一个iframe,并将其src属性设置为我们想要通信的页面B,并将我们想要传递的参数作为hash值传过去,
如iframe.src = localhost:8080/test2/index.jsp#MyParme;
然后在localhost:8080/test2/index.jsp页面(即B页面)下,写一些对hash值处理的函数,如:
switch(location.hash){ case '#MyParam': callBack(); break; case '#paramset': //do something…… break; } function callBack(){ try { parent.location.hash = 'somedata'; } catch (e) { // ie、chrome的安全机制无法修改parent.location.hash, // 所以要利用一个中间的cnblogs域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意该文件在"a.com"域下 document.body.appendChild(ifrproxy); } }
然后在回调函数中将传回的参数值作为A页面的hash值,由于部分浏览器不允许在不同域的情况下修改parent.location.hash,所以要在中间添加一个代理器,即页面C,该页面与A页面同域。
C页面:
parent.parent.location.hash = self.location.hash.substring(1);
该方法会将所有参数暴露在URL中。
第四种:window.name.
与上面的方法类似,在B页面中设置window.name='你想要传的数据',接着在a页面中获取该iframe下的window.name.
第五种:HTML5的postMessage
postMessage方法有两个参数,第一个为我们要传送的值,第二个为我们接受方的域。
postMessage可以通过监听message事件完成跨域操作,如下:
传送的数据的页面A:
<html> <head> </head> <body> <iframe src="http://localhost:8080/test2/index.jsp"></iframe> </body> </html> <script> window.onload=function(){ var ifr = document.getElementsByTagName("iframe")[0]; //传送数据给B页面 ifr.contentWindow.postMessage('MyParme','http://localhost:8080'); } //监听message事件 当B页面返回数据时触发 window.addEventListener('message',function(e){ var Parme=e.data; console.log(Parme); },false); </script>
接受端B页面
<script type="text/javascript"> window.addEventListener('message',function(e){ console.log(e.data) //将数据返回给A页面 window.parent.postMessage("Hello world",'*'); },false); </script>
代码运行图:
本文参考自:http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html#m3