AJAX跨域问题

    这几天被跨域问题困扰了很久,现在整理下思绪,以备不时之需。
  

     由于安全方面的考虑,JS被限制了跨域访问的能力,但是有时候我们希望能够做一些合理的跨域访问的事情,那么怎么办呢?


这里分两类情况:


一、基于同一父域的子域之间页面的访问;参见如下3个domain域:sina.com、news.sina.com、mail.sina.com;它们有相同的父域taobao.com。


二、基于不同父域页面之间的访问;参见如下3个domain域:taobao.com、baidu.com、sina.com.cn;它们具有不同的父域。


根据不同的跨域需求,跨域技术可以归为下面几类:

 


方案1:服务器Proxy


域A 的页面JS需要访问域B下的链接获取数据,该方案在域A的服务器端建立一个Proxy程序(可能是PHP\ASP、servlet等任何服务端程序),域A的页面 JS直接调用本域下的Proxy程序,proxy程序负责将请求发送给域B下的链接并获取到数据,最后再通过Proxy将数据返回给页面JS使用。


经过的访问流程就是: 域A下JS --> 域A 下Proxy -- > 域B下的链接


Proxy方案优点是可以适用用于几乎所有的跨域访问,而且只需要要一个域中进行开发,另一个域可以提供任何类型格式的数据。缺点是这种方案经过了中间Proxy,所以延迟可能稍微大一点,并且会加重本域服务器的负荷,开发工作量也稍微大一点。


方案2:隐藏iframe、共享domain :


通常情况下,我们会用与下面类似的代码来创建一个XMLHttpRequest对象:


factories = [

function() { return new XMLHttpRequest(); },

function() { return new ActiveXObject("Msxml2.XMLHTTP"); },

function() { return new ActiveXObject("Microsoft.XMLHTTP"); }

];

function newRequest() {

 for(var i = 0; i < factories.length; i++) {

   try{

    var factory = factories[i];

    return factory();

  } catch(e) {}

 }

 return null;

}



 

上面的代码中引用ActiveXObject,是为了兼容IE6系列浏览器。每次我们调用newRequest函数,就获得了一个刚刚创建的 Ajax对象,然后用这个Ajax对象来发送HTTP请求。例如,下面的代码向abc.example.com发送了一个GET请求:


var request = newRequest();

request.open("GET", "http://abc.example.com" );

request.send(null);


 

假设上面的代码包含在一个abc.example.com域名下的页面里,则这个GET请求可以正常发送成功,没有任何问题。然而,如果现在要向def.example.com发送请求,则出现跨域问题,JavaScript引擎抛出异常。


解决的办法是,在def.example.com域下放置一个跨域文件,假设叫crossdomain.html;然后将前面的 newRequest函数的定义移到这个跨域文件中;最后像之前修改document.domain值的做法一样,在crossdomain.html文件和 abc.example.com域下调用Ajax的页面顶端,都加上:


 

<script type="text/javascript">

document.domain = "example.com";

</script>


 

 

为了使用跨域文件,我们在abc.example.com域下调用Ajax的页面中嵌入一个隐藏的指向跨域文件的iframe,例如:

<iframe name="xd_iframe" style="display:none" src="http://def.example.com/crossdomain.html"></iframe>

这时abc.example.com域下的页面和跨域文件crossdomain.html都在同一个域(example.com)下,我们可以在abc.example.com域下的页面中去调用crossdomain.html中的newRequest函数:

var request = window.frames["xd_iframe"].newRequest();

这样获得的request对象,就可以向http://def.example.com发送HTTP请求了。


隐藏iframe方式也很简单,它可以处理任何返回的数据格式,但它只适用在具有同一个父域下的跨域请求上,并且要求其他域得配合开发,即需要设置document.domain。


方案3: JSONP 跨域GET请求 通过Script标签) :


在域A页面http://news.sina.com/test.htm 的head中写一个空的Script标签:


 

<html>  
<head>  
 <mce:script id="remoteScript" type="text/javascript" src=""/><!--  
<head>  
<body>  
<script type="text/javascript" >  
 Var remoteScript=document.getElementById("remoteScript");  
 remoteScript.src="www.baidu.com/xxxxx.do";//域B的链接  
 alert(remote.test);//使用域B返回的JSON数据   
 alert(f[0]);  
// --></mce:script>  
</body>  
</html> 







 [注意]:这种方案要求域B返回的数据必须是合法的JSON格式或者如JS文件的格式;



JSONP 的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要 XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。


JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。而且它 对返回的数据格式要求有点严格,只能是Json格式数据,如果是其他格式的数据,那么这种方法就无能为力了。


方案4: 利用flash实现跨域HTTP请求


据称,flash在浏览器中的普及率高达90%以上。


flash代码和JavaScript代码之间可以互相调用,并且flash的“安全沙箱”机制与JavaScript的安全机制并不尽相同,因此,我们可以利用flash来实现跨域提交HTTP请求(支持GET/POST等)。


例如,我们用浏览器访问http://example1.com/index.php这个页面,在这个页面中引用了http: //example2.com /flash.swf这个flash文件,然后在flash代码中向http://example3.com/webservice.php发送HTTP 请求。


这个请求能否被成功发送,取决于在example3.com的根路径下是否放置了一个crossdomain.xml以及这个crossdomain.xml的配置如何。flash的“安全沙箱”会保证:仅当example3.com服务器在根路径下确实放置了 crossdomain.xml文件并且在这个文件中配置了允许接受来自example2.com的flash的请求时,这个请求才能真正成功。下面是一个crossdomain.xml文件内容的例子:


<?xml version="1.0"?>

<cross-domain-policy>

<allow-access-from domain="example2.com" />

</cross-domain-policy>


方案5: window.postMessage


window.postMessage是HTML标准的下一个版本HTML5 支持的一个新特性。受当前互联网技术突飞猛进的影响,浏览器跨域通信的需求越来越强烈,HTML标准终于把跨域通信考虑进去了。但目前HTML5仍然只是一个draft。


window.postMessage是一个安全的实现直接跨域通信的方法。但是目前并不是所有浏览器都能支持,只有Firefox 3、Safari 4和IE8可以支持这个调用。


使用它向其它窗口发送消息的调用方式大概如下:


otherWindow.postMessage(message, targetOrigin);

 

在接收的窗口,需要设置一个事件处理函数来接收发过来的消息:

 

window.addEventListener("message", receiveMessage, false);function receiveMessage(event){ if (event.origin!== "http://example.org:8080") return;}消息包含三个属性:data、origin(携带发送窗口所在域的真实信息)和source(代表发送窗口的handle)。


安全性考虑:使用window.postMessage,必需要使用消息的origin和source属性来验证发送者的身份,否则会造成XSS漏洞。


window.postMessage在功能上同iframe实现的跨域功能同样强大,并且使用简单,效率更高,但缺点是它目前在浏览器兼容方面有待提高。


方案6: 通过iframe实现跨域。


iframe跨域的方式,功能强于JSONP,它不仅能用来跨域完成HTTP请求,还能在前端跨域实现JavaScript调用。因此,完全不同域的跨域问题,通常采用iframe的方式来解决。

 

与 JSONP技术通过创建<script>节点向不同的域提交GET请求的工作方式类似,我们也可以通过在http://example1.com/index.php页面中创建指向http://example2.com/getinfo.php的 iframe节点跨域提交GET请求。然而,请求返回的结果无法回调http://example1.com/index.php页面中的 callback函数,因为受到“同源策略”的影响。


为了解决这个问题,我们需要在example1.com下放置一个跨域文件,比如路径是http://example1.com/crossdomain.html。


当http://example2.com/getinfo.php这个请求返回结果的时候,它大体上有两个选择。

 

第一个选择是,它可以在iframe中做一个302跳转,跳转到跨域文件http://example1.com /crossdomain.html,同时将返回结果经过URL编码之后作为参数缀在跨域文件URL后面,例如http://example1.com /crossdomain.html?result=<URL-Encoding-Content>。

 

另一个选择是,它可以在返回的页面中再嵌入一个iframe,指向跨域文件,同时也是将返回结果经过URL编码之后作为参数缀在跨域文件URL后面。


在跨域文件中,包含一段JavaScript代码,这段代码完成的功能,是从URL中提取结果参数,经过一定处理后调用原来的 http://example1.com/index.php页面中的一个预先约定好的callback函数,同时将结果参数传给这个函数。

 

http: //example1.com/index.php页面和跨域文件是在同一个域下的,因此这个函数调用可以通过。跨域文件所在iframe和原来的 http://example1.com/index.php页面的关系,在前述第一种选择下,后者是前者的父窗口,在第二种选择下,后者是前者的父窗口的父窗口。


根据前面的叙述,有了跨域文件之后,我们就可以实现通过iframe方式在不同域之间进行JavaScript调用。这个调用过程可以完全跟 HTTP请求无关,例如有些站点可以支持动态地调整在页面中嵌入的第三方iframe的高度,这其实是通过在第三方iframe里面检测自己页面的高度变化,然后通过跨域方式的函数调用将这个变化告知父窗口来完成的。

 

既然利用iframe可以实现跨域 JavaScript调用,那么跨域提交POST请求等其它类型的HTTP请求就不是难事。例如我们可以跨域调用目标域的JavaScript代码在目标域下提交Ajax请求(GET/POST/etc.),然后将返回的结果再跨域传原来的域。


使用iframe跨域,优点是功能强大,支持各种浏览器,几乎可以完成任何跨域想做的事情;缺点是实现复杂,要处理很多浏览器兼容问题,并且传输的数据不宜过大,过大了可能会超过浏览器对URL长度的限制,要考虑对数据进行分段传输等。


方案7: getJSON+callback方法。

在加载了jquery的前提下,用法如下:

$.post($action,{'type':'us'},function callback($data,$code){
   if($code != 'success'){
    alert('aa');

   }else{   
    alert('hello!');

   }
  },"json"); 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值