跨域请求详解

同源策略

Ajax的一个限制是同源策略(same origin policy),它要求所有请求必须来自同一个域名、子域名,并且地址的端口也应当一致。主要原因是处于安全考虑:因为当一个ajax请示被发送,所有的请求都会附带主域的cookie信息一起发送。也就是说,对于远程服务来讲,请求如果是来自于登陆后的用户,若没有同源策略的限制,攻击者就有可能获取你的Gmail里的邮件、得到你的 Fackbook 状态或者你 Twitter 中的好友,这是一个非常严重的安全性问题。

但是,尽管出于安全问题的考虑而提出了同源策略,这也对那些的确需要跨域获取合法数据的开发者造成了一些不方便。诸如Adobe Flash和JAVA之类的技术已经着手解决了这个问题,通常是使用跨域策略文件。现在针对Ajax,也提出了CORS(cross-origin resource sharing)的标准规范。

CORS

CORS打破同源策略的限制,赋予了前端代码访问可信的远程服务的权限。主流的浏览器都很好的支持这个规范,除非使用IE6,基本上可以很好地使用它。

支持CORS的浏览器:

  • IE>=8(需要安全caveat)
  • Firefox>=3
  • Safari:完全支持
  • Chrome:完全支持
  • Opera:不支持

CORS的使用非常简单。如果想将你的服务器添加为受信任的数据源,只需在HTTP协议的响应头里加几行:

Access-Control-Allow-Origin:example.com

Access-Control-Request-Method:GET,POST

这两个头字段会对来自example.com的跨域GET和POST请求做验证。多个值之间用逗号分隔,就像上面提到的GET,POST值一样。如果要添加多个域名,将域名列在 Access-Control-Allow-Origin头字段之中,每两个域名之间用逗号分隔。如果允许来自任意域的访问请求,则需要在源中加入通配符(*)。

对于有些浏览器,比如Safari,它会首先发起一个OPTIONS请求以检查服务器是否允许跨域的请求。

另一方面,Firefox则会直接发起跨域请求,但当服务器没有配置CORS头字段时会抛出一个安全异常。需要注意一下这种浏览器行为的不同。

你甚至可以使用 Access-Control-Allow-Headers头字段来认证自定义的请求头:

Access-Control-Request-Headers:Authorization

这也意味着客户端可以在Ajax请求中添加自定义头,比如使用开放认证(OAuth)对请求进行签名:

var req=new XMLHttpRequest();

req.open("POST","/endpoint",true);

req.setRequestHeader("Authorization",oauth_signature);

XDomainRequest

不幸的是,尽管CORS是可以正常工作在IE8及更高版本的IE中的,微软还是选择另辟蹊径,不兼容规范且对W3C工作组制定的标准视而不见。微软使用了一个自己的对象XDomainRequest,用来代替XMLHttpRequest进行跨域通信。它的接口和XMLHttpRequest的非常像,它包含一系列约束和限制,比如只支持GET和POST方法,不支持验证和自定义字段,并且支持“Content-Type:text/plain"类型的请求。

如果你满足来这些限制条件,就可以在IE8中使用正确的Access-Control头字段和XDomainRequest来实现CORS,网页可以直接在浏览器中生成跨域数据请求,而不必使用服务器到服务器的请求。跨域请求需要经过网页和服务器的相互同意。通过利用window对象创建XDomainRequest对象,并打开到特定域的连接,可以在网页中启动一个跨域请求。浏览器将通过发送带有源值的Origin请求头,从特定域的服务器中请求数据。如果服务器响应的Access-Control-Allow-Origin响应头为*或请求页面的确切URL,则浏览器将完成连接,然后可以使用XDomainRequest跨域请求目标服务器上的数据。XDomainRequest使用如下所示:

// 1. Create XDR object: 
var xdr = new XDomainRequest(); 

// 2. Open connection with server using GET method:
xdr.open("get", "http://www.contoso.com/xdr.aspx");

// 3. Send string data to server:
xdr.send();

xdr.onload=function(){
var data=JSON.parse(xdr.responseText);
};

XDomain支持的方法有onload,onerror,ontimeout,onprogress,timeout。

JSONP

JSONP(JSON with padding)很早之前就被标准化了,甚至在CORS之前。这是另一种从远程服务器抓取数据的方式。原理是通过创建一个script标签,所请求的外部文件包含一段JSON数据,数据是由服务器所返回的,作为参数包装在一个函数调用中。script标签获取脚本文件并不受跨域的限制,所有浏览器都支持这种技术。

下面的例子是一个script标签指向一个远程服务:

<script src="http://example.com/data.json"></script>

所请求的文件data.json中包含一个JSON对象,这个对象包装在一个函数调用中:

jsonCallback({"data":"foo"})

这时我们定义一个全局函数,当加载脚本后,这个函数就会被调用:

window.jsonCallback=function(result){

//处理返回结果的相关逻辑

}

jQuery将这个过程包装成了简洁API:

jQuery.getJSON("http://example.com/data.json?callback=jsonCallback",function(result){
console.log(result);
});

或者

  $.ajax({  
        url:"http://crossdomain.com/services.php",  
        dataType:'jsonp',  
        data:'',  
        jsonp:'callback',  
        success:function(result) {  
        },  
        timeout:3000  
    }); 

 

jQuery将上面的URL中最后的问号替换为一个由它创建的随机命名的临时函数。服务器会获取这个callback参数,使用这个名字作为回调函数名称返回给客户端。

 参考资料:

《基于MVC的Javascript Web富应用开发》

http://msdn.microsoft.com/zh-cn/library/dd573303%28v=vs.85%29.aspx

posted @ 2014-08-12 17:31  yuki.idesign  阅读(776)  评论(0编辑  收藏  举报