本文转自:
javascript跨域有两种情况:
1、基于同一父域的子域之间,如:a.c.com和b.c.com
2、基于不同的父域之间,如:
3、端口的不同,如:和
4、协议不同,如:
对于情况3和4,需要通过后台proxy来解决,具体方式如下:
a、在发起方的域下创建proxy程序
b、发起方的js调用本域下的proxy程序
c、proxy将请求发送给接收方并获取相应数据
d、proxy将获得的数据返回给发起方的js
发起方页面代码如下:
<form id="form1" runat="server">
<div> <input type="text" id="txtSrc" value="" style="width: 378px" /> <input id="btnProxy" type="button" value="通过Proxy获取数据" οnclick="GetDataFromProxy();" /><br /> <br /> <br /> </div> <div id="divData"></div> </form></body><script language="javascript" type="text/javascript"> function GetDataFromProxy() { var src = document.getElementById('txtSrc').value; var request = null; if (window.XMLHttpRequest) { request = new XMLHttpRequest(); } else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }request.onreadystatechange = function() {
var ready = request.readyState; var data = null; { if (ready == 4) { data = request.responseText; document.getElementById('divData').innerHTML = data; } else { document.getElementById('divData').text = "Loading"; } } }var url = "Proxy.ashx?src=" + escape(src);
request.open("get",url,false); request.send(null); } </script>发起方Proxy代码如下:
using System.Data;
using System.Linq;using System.Web;using System.Web.Services;using System.Web.Services.Protocols;using System.Xml.Linq;using System.IO;using System.Net;using System.Text; namespace WebApplication1{ /// <summary> /// Summary description for $codebehindclassname$ /// </summary> [WebService(Namespace = "")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] public class Proxy : IHttpHandler { const int BUFFER_SIZE = 8 * 1024; public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string src = context.Request["src"];WebRequest wr = WebRequest.Create(src);
WebResponse wres = wr.GetResponse(); Encoding resEncoding = System.Text.Encoding.GetEncoding("gb2312"); StreamReader sr = new StreamReader(wres.GetResponseStream(), resEncoding); string html = sr.ReadToEnd(); sr.Close(); wres.Close();context.Response.Write("<br/><br/><br/><br/>");
context.Response.Write(html); }public bool IsReusable
{ get { return false; } } }}而情况1和2除了通过后台proxy这种方式外,还可以有7种办法来解决:
1、document.domain+iframe(只能解决情况1):
a、在发起方页面和接收方页面设置document.domain,并将值设为父域的主域名(window.location.hostname)
b、在发起方页面创建一个隐藏的iframe,iframe的源是接收方页面
c、根据浏览器的不同,通过iframe.contentDocument || iframe.contentWindow.document来获得接收方页面的内容
d、通过获得的接收方页面的内容来与接收方进行交互
这种方法有个缺点,就是当一个域被攻击时,另一个域会有安全漏洞出现。
发起方页面代码如下:
<body>
<div> <input type="text" id="txtSrc" value="" style="width: 378px" /> <input id="btnDomain" type="button" value="通过Domain获取数据" οnclick="GetDataFromDomain();" /><br /> <br /> <br /></div><div id="divData"></div> </body><script language="javascript" type="text/javascript"> document.domain = 'a.com'; var src = document.getElementById('txtSrc').value; var ifr = document.createElement('iframe'); ifr.src = src; ifr.style.display = 'none'; document.body.appendChild(ifr); function GetDataFromDomain() { var doc = ifr.contentDocument || ifr.contentWindow.document; alert(doc.getElementById("data").value); }</script>接收方页面代码如下:
<body>
<input type="hidden" id="data" value="Cross Domain" style="width: 378px" /></body><script language="javascript" type="text/javascript"> document.domain = 'a.com'; </script>2、动态创建script:
a、在发起方页面动态加载一个script,script的URL指向接收方的一个处理地址(后台),该地址返回的javascript方法会被执行,另外URL中可以传入一些参数,该方法只支持GET方式提交参数。
b、加载的script可以在调用跨域js方法后再做一些自己的处理
发起方页面的代码如下:
<head>
<title>Script Test</title> <script language="javascript" type="text/javascript"> function load_script(callback){ var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); var src = document.getElementById('txtSrc').value; script.type = 'text/javascript'; script.src = src; //借鉴了jQuery的script跨域方法 script.onload = script.onreadystatechange = function(){ if((!this.readyState||this.readyState === "loaded"||this.readyState === "complete")){ callback && callback(); // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } } }; // Use insertBefore instead of appendChild to circumvent an IE6 bug. head.insertBefore( script, head.firstChild ); } </script></head><body><input type="text" id="txtSrc" value="" style="width: 378px" /><input type="button" value="通过动态创建script标签来获取数据" οnclick="load_script(function(){alert('动态加载script标签成功')})"/></body>接收方服务器端代码如下:
protected void Page_Load(object sender, EventArgs e)
{ Response.Clear(); Response.ContentType = "application/x-javascript"; Response.Write(String.Format(@"alert('{0}');", DateTime.Now)); Response.End();}3、location.hash+iframe:
a、发起方创建一个隐藏的iframe,iframe的源指向接收方的页面,并通过接收方页面的hash值来传送数据
b、发起方创建一个定时器,定时检查自己的location.hash并作相应的处理
c、接收方创建一个隐藏的iframe,iframe的源指向发起方所在域的一个代理页面,并将接收方根据发起方传入的数据而处理后的数据通过代理页面的hash值来传送
d、接收方创建一个定时器,定时检查自己的location.hash并作相应的处理
e、代理页面创建一个定时器,定时检查自己的location.hash并同步更新发起方页面的hash值
#aaa就是location.hash值
发起方页面代码如下:
<body>
<div><input type="text" id="txtSrc" value="1" style="width: 378px" /><input id="btnAddHash" type="button" value="添加Hash值" οnclick="addHash();" /><iframe id="ifr1" style="display:none"></iframe></div></body><script language="javascript" type="text/javascript"> function addHash() { var src = document.getElementById('txtSrc').value; if (src.length > 0) { changeHash(src); } } function changeHash(src) { if (document.getElementById('ifr1')) { var ifr = document.getElementById('ifr1'); ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src; } else { var ifr = document.createElement('iframe'); ifr.setAttribute('id', 'ifr1'); ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src; ifr.style.display = 'none'; document.body.appendChild(ifr); } } function checkHash() { if (location.hash && location.hash.length > 1) { changeHash(location.hash.substring(1)); } } setInterval(checkHash, 2000);</script>接收方页面代码如下:
<body>
<iframe id="ifr2" style="display:none"></iframe></body><script language="javascript" type="text/javascript"> function checkHash() { if (location.hash && location.hash.length > 1) { var hashData = location.hash.substring(1); var ifr = null; if (document.getElementById('ifr2')) { ifr = document.getElementById('ifr2'); } else { ifr = document.createElement('iframe'); ifr.setAttribute('id', 'ifr2'); ifr.style.display = 'none'; document.body.appendChild(ifr); } switch (hashData) { case '1': alert('One'); if (ifr) { ifr.src = 'http://www.a.com/test/HashTest3.htm#2'; } break; case '2': alert('Two'); if (ifr) { ifr.src = 'http://www.a.com/test/HashTest3.htm#1'; } break; default: break; } } } setInterval(checkHash, 2000);</script>发起方域下的代理页面代码如下:
<body></body>
<script language="javascript" type="text/javascript"> function checkHash() { if (parent && parent.parent && parent.parent.location && self.location.hash.length > 1) { parent.parent.location.hash = self.location.hash.substring(1); } } setInterval(checkHash, 500);</script>4、window.name:
a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、接收方在自己页面通过script将需要传送的数据放入window.name里
c、发起方在iframe的onload方法里将iframe的源改为和自己在同一个域下的代理页面(因为只能是同一个域下才能访问window.name的值)
d、获取window.name的值(虽然iframe的源改变了,但是window.name的值不会变)
window.name的值差不多可以有2MB大小
发起方页面代码如下:
<body>
<div><input id="btnName" type="button" value="通过window.name获取数据" οnclick="getData();" /><iframe id="ifr1" style="display:none" src="></div></body><script language="javascript" type="text/javascript"> var ischanged = false; function changeSrc() { if (document.getElementById('ifr1')) { var ifr = document.getElementById('ifr1'); if (!ischanged) { ischanged = true; ifr.contentWindow.location = 'http://www.a.com/Test/NameTest3.htm'; } else { var data = ifr.contentWindow.name; alert(data); } } else { var ifr = document.createElement('iframe'); ifr.setAttribute('id', 'ifr1'); ifr.src = 'http://www.b.com/Test/NameTest2.htm'; ifr.style.display = 'none'; document.body.appendChild(ifr); } } function getData() { setInterval(changeSrc, 2000); }</script>接收方页面代码如下:
<body></body>
<script language="javascript" type="text/javascript"> window.name = 'NameTest2';</script>发起方域下的代理页面代码如下:
<body></body>
(其实什么都不用写)
5、HTML5的postMessage
<body></body>
<script language="javascript" type="text/javascript"> window.addEventListener('message', function(event) { if (event.origin == 'http://www.a.com') { alert(event.data); alert(event.source); } }, false);</script>6、window.opener(适用于IE6、7,也就是operner hack方法,不过貌似现在已经不管用了,只要打过微软的安全补丁.kb2497640就不能用了)
a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、发起方页面通过iframe.contentWindow.opener = {a: function(params){...}, b: function(params){...} ...}来定义可被接收方调用的方法
c、接收方页面通过window.opener.a/window.opener.b来调用发起方定义的方法
d、接收方页面通过parent.opener = {c: function(params){...}, d: function(params){...} ...}来定义可被发起方调用的方法
e、发起方页面通过opener.c/opener.d来调用接收方定义的方法
其实原理就是重置opener对象
发起方页面代码如下:
<body>
<iframe id="ifr" src="" style="display:none"></iframe></body><script language="javascript" type="text/javascript"> var ifr = document.getElementById('ifr'); ifr.contentWindow.opener = { a: function(msg) { alert('我调用了a方法获得了消息:' + msg); } } </script>接收方页面代码如下:
<body>
</body><script language="javascript" type="text/javascript"> window.opener.a('aaa'); </script>7、window.navigator(适用于IE6、7,貌似现在还能用,还没被补丁掉)
a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面
b、发起方页面通过window.navigator.a = function(params){...}; window.navigator.b = function(params){...}; 来定义被接收方调用的方法
c、接收方页面通过window.navigator.a(params); window.navigator.b(params);来调用发起方定义的方法
d、接收方页面通过window.navigator.c = function(params){...}; window.navigator.d = function(params){...}; 来定义被发起方调用的方法
e、发起方页面通过window.navigator.c(params); window.navigator.d(params);来调用接收方定义的方法
发起方页面代码如下:
<body>
<iframe id="ifr" src="" style="display:none"></iframe></body><script language="javascript" type="text/javascript"> window.navigator.a = function(msg) { alert('我调用了a方法获得了消息:' + msg); } window.navigator.b = function(msg) { alert('我调用了b方法获得了消息:' + msg); } setInterval(function() { window.navigator.c('ccc'); }, 2000); setInterval(function() { window.navigator.d('ddd'); }, 2000);</script>接收方页面代码如下:
<body>
</body><script language="javascript" type="text/javascript"> window.navigator.c = function(msg) { alert('我调用了c方法获得了消息:' + msg); } window.navigator.d = function(msg) { alert('我调用了d方法获得了消息:' + msg); } setInterval(function() { window.navigator.a('aaa'); }, 2000); setInterval(function() { window.navigator.b('bbb'); }, 2000);</script>