解决xmlhttp的跨域访问权限


一般情形下,为安全起见,浏览器不允许你在客户端通过XMLHttpRequest访问别的域(参考连接1,2),即使是同一域的子域也不行,譬如www.joycode.com 到 blog.joycode.com。(你可以通过某些设置来访问子域,但因为这方法不是很通行,所有就不考虑了,但如果你感兴趣,参考连接2。)  


但很明显,在不少情形下,访问别的网站,获取别的网站的信息/服务是非常有用的,特别是在这个Web 2.0时代。


常用的跨站访问的方法有3种(参考连接3,4):



在同一域的服务器端建立一个代理,浏览器向该代理网址发送请求,然后该代理向其他域的网址发请求,在获取回复后,或作处理或按原样发回到浏览器


使用按需(On-Demand) Javascript 脚本。在页面内动态生成新的<script>,将其src属性指向别的网站的网址,这个网址返回的内容必须是合法的Javascript脚本,常用的是JSON消息。


使用IFRAME。在页面内嵌或动态生成指向别的网站的IFRAME,然后这2个网页间可以通过改变对方的anchor hash fragment来传输消息。改变一个网页的anchor hash fragment并不会使浏览器重新装载网页,所以一个网页的状态得以保持,而网页本身则可以通过一个计时器(timer)来察觉自己anchor hash的变化,从而相应改变自己的状态(参考这个帖子中提及的Nikhil Kothari的历史控件中的方法)。 Julien Couvreur在他的《Cross-document messaging hack》里描述了一个更复杂的应用情形,


"....


For example, if you have page A containing an iframe B in a different domain,then B can create a new iframe and load it with a url in the same domain as A. The url that is loaded doesn't generate a request to the server if it is properly cached and only the fragment identifier is used to pass changing information. Page A can now get the DOM handle on the new iframe and successfully retrieve the information transmitted in the url by B...." (大体这样,网页A包含了一个IFRAME B,B的网页来自一个不同的域。然后B页可以生成一个IFRAME C,把它指向与网页A同域的某个地址,因为是A与C同域,网页A可以访问C里的信息,反之亦然。)


ASP.NET AJAX扩展(即Atlas)提供了一个桥(bridge)机制让你在服务器端配置来访问别的网站,并同时支持POX和SOAP这2种协议。想了解其中细节,请参考Atlas文档里的《Building Mash-ups with "Atlas"》。当然你完全可以自己建立一个web service,通过它来访问其他网站并返回信息。


据说,Atlas中的 IFrameExecutor 可以实现跨域的调用,我按照MSDN博客Federal Developer Weblog的这篇帖子《Calling web services hosted outside of your application with “Atlas”》上的步骤试了一下,但在Windows 2003 Server SP1上得到却是“Access is denied”的错误信息。然后我下载了该文中的项目,试验的结果仍旧是“Access is denied”。也许需要改动一些浏览器中的什么设置才能成功,但这不是我的目的,我需要一个在普通设置下都能成功的例子。


按需(On-Demand) Javascript脚本的实现是很简单的,譬如我有这样一个网页,(想测试的话,需要改动其中的网址)






<html>



<head>



<script language="javascript" type="text/javascript">



function loadContent()



{



var s=document.createElement('SCRIPT');



s.src='http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent';



document.body.appendChild(s);



}



function setDivContent(v)



{



var dv = document.getElementById("dv");



dv.innerHTML = v;



}



</script>



</head>



<body>



<div id="dv"></div>



<input type="button" value="Click Me" onclick="loadContent()">



</body>



</html>


复制代码


其中的www.anotherdomain.com/TestCrossJS.aspx是这样的,




<script language="C#" runat="server">



void Page_Load(object sender, EventArgs e)



{



   string f = Request.QueryString["f"];



   Response.Clear();



   Response.ContentType = "application/x-javascript";



   Response.Write(String.Format(@"



                    {0}('{1}');",



                    f,



                    DateTime.Now));



   Response.End();



}



</script>


复制代码


点击“Click Me”按钮,生成一个新的script tag,下载对应的 Javascript 脚本,结束时回调其中的setDivContent(),从而更新网页上一个div的内容。


IFRAME的方法好像很流行,除了dojo工具包支持外,据微软的Dare Obasanjo说(参考连接9),Windows Live Contacts Gadget使用了这个方法来获取Hotmail的address book。最近,Plaxo公司的开发人员 Joseph Smarr在七月的OSCON 2006会议上作了一个题为《Cross-site Ajax: Challenges and Techniques for Building Rich Web 2.0 Mashups》的讲座[来源:Kevin Yank--OSCON 2006: Cross-site Ajax],他们将这个方法做成了一个平台,允许合作伙伴间合作,他们开发的方案叫“The JavaScript Wormhole(虫洞)”,据说准备将其推广为一个标准。他讲座的PPT可以在这里下载,里面对这个方案做了说明,非常值得看一下。


现在将IFRAME的方法简单示范如下:


1. http://domain1/TestCross.html:






<html>



<head>



<script language="javascript" type="text/javascript">



var url = "http://domain2/TestCross.html"



var oldHash = null;



var timer = null;



function getHash()



{



var hash = window.location.hash;



if ((hash.length >= 1) && (hash.charAt(0) == '#'))



{



hash = hash.substring(1);



}



return hash;



}



function sendRequest()



{



var d = document;



var t = d.getElementById('request');



var f = d.getElementById('alienFrame');



f.src = url + "#" + t.value + "<br/>" + new Date();



}



function setDivHtml(v)



{



var d = document;



var dv = d.getElementById('response');



dv.innerHTML = v;



}



function idle()



{



var newHash = getHash();



if (newHash != oldHash)



{



setDivHtml(newHash);



oldHash = newHash;



}



timer = window.setTimeout(idle, 100);



}



function window.onload()



{



timer = window.setTimeout(idle, 100);



}



</script>



</head>



<body>



请求:<input type="text" id="request"> <input type="button" value="发送" onclick="sendRequest()" /><br/>



回复:<div id="response"></div>



<iframe id="alienFrame" src="http://domain2/TestCross.html"></iframe>



</body>



</html>


复制代码


2. http://domain2/TestCross.html:






<html>



<head>



<script language="javascript" type="text/javascript">



var url = "http://domain1/TestCross.html"



var oldHash = null;



var timer = null;



function getHash()



{



var hash = window.location.hash;



if ((hash.length >= 1) && (hash.charAt(0) == '#'))



{



hash = hash.substring(1);



}



return hash;



}



function sendRequest()



{



var d = document;



var t = d.getElementById('request');



var f = parent;



//alert(f.document); //试着去掉这个注释,你会得到“Access is denied”



f.location.href = url + "#" + t.value + "<br/>" + new Date();



}



function setDivHtml(v)



{



var d = document;



var dv = d.getElementById('response');



dv.innerHTML = v;



}



function idle()



{



var newHash = getHash();



if (newHash != oldHash)



{



setDivHtml(newHash);



oldHash = newHash;



}



timer = window.setTimeout(idle, 100);



}



function window.onload()



{



timer = window.setTimeout(idle, 100);



}



</script>



</head>



<body>



请求:<input type="text" id="request"> <input type="button" value="发送" onclick="sendRequest()" /><br/>



回复:<div id="response"></div>



</body>



</html>


复制代码


两个网页基本相同,第一个网页内嵌一个IFRAME,在点击“发送”按钮后,会将文本框里的内容通过hash fragment传给IFRAME。点击IFRAME里的“发送”按钮后,它会将文本框里的内容通过hash fragment传给父窗口。因为是只改动了hash fragment,浏览器不会重新load网页内容,这里使用了一个计时器来检测URL变化,如果变化了,就更新其中一个div的内容 。



这个方法是不是个安全漏洞?考虑到微软的Windows Live都在使用这个方法,估计不是,。这个方法是不是很安全?考虑到这个方法只有在2个网站协作的情形才能成功,安全问题好像不是很大,除非其中涉及的网站本身有XSS的问题。

转载请注明来自: http://www.caodong.net/Article/121.html