第17章 Ajax 与 JSON (二)

 

17.2 跨域请求

通过 XHR 实现 Ajax 通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。但是,实现合理的跨域请求对开发某些浏览器应用程序也是至关重要的。

实现跨域请求的解决方案可能会有所差异,但它们的目标是类似的。首先,就是要确保不会在请求和响应中携带 cookie,因为 cookie 被窃取是主要的安全隐患。其次,就是要确保未经授权无法访问相应的资源。简言之,如果某个域的资源并没有允许访问,JavaScript 就不应该能够请求该域中的资源。为此,IE 和 Firefox 已经实现了各自的跨域解决方案。

17.2.1 XDomainReuqest 对象

微软在 IE8 中引入了 XDR (XDomainRequest) 类型。这种对象与 XHR 类似,但能实现安全可靠的跨域通信。XDR 对象的安全机制中部分实现了 W3C 的跨站点请求访问控制规范(Access Control for Cross-Site Request) 。以下是 XDR 与 XHR 的一些不同之处。

  • cookie 不会随请求发送,不会随响应返回。
  • 只能设置请求头部信息中的 Content-Type 字段。
  • 不能访问响应头部信息。
  • 只支持 GET 和 POST 请求。
  • XDR 只能访问 Access-Control-Allow-Origin 头部字段设置为 * 的资源。 
这些变化使 CSRF 和 XSS (Cross-Site Scripting,跨站点脚本) 的问题得到了缓解。被请求的资源可以根据它认为合适的任意数据 (用户代理、来源页面等) 来决定是否设置 Access-Control-Allow-Origin 头部。 与请求一起发送的还有一个 Origin 头部,其值为请求的来源域,以便远程资源明确地识别 XDR 请求。

XDR 对象的使用方法与 XHR 对象非常相似。也是创建一个 XDomainRequest 的实例,调用 open() 方法,再调用 send() 方法。但与 XHR 对象的 open() 方法不同,XDR 对象的 open() 方法只接受两个参数:请求的类型和 URL 。

所有 XDR 请求都是异步执行的,不能用它来创建同步请求。请求返回之后,会触发 load 事件,响应的数据也会保存在 responseText 属性中,如下所示:

var xdr = new XDomainRequest();

xdr.onload = function(){

alert(xdr.responseText);

};

xdr.open("get", "http://www.somewhere-else.com/page/");

xdr.send(null);

在接收到响应后,你只能访问响应的原始文本;没有办法确定响应的状态代码。而且,只要响应有效就会触发 load 事件,如果失败 (包括响应中缺少 Access-Control-Allow-Origin 头部) 就会触发 error 事件。遗憾的是,出了错误本身之外,没有其他信息可用,因此唯一能够确定的就只有未成功了。要检测错误,可以像下面这样指定一个 onerror 事件处理程序;

var xdr = new XDomainRequest();

xdr.onload = function(){

alert(xdr.responseText);

};

xdr.onerror = function(){

alert("An error occurred.");

};

xdr.open("get", "http://www.somewhere-else.com/page/");

xdr.send(null);

鉴于导致 XDR 请求失败的因素很多,因此建议你不要忘记通过 onerror 事件处理程序来捕获该事件;否则,即使请求失败也不会有任何提示。

在请求返回前调用 abort() 方法可以终止请求:

xdr.abort();             // 终止请求

与 XHR 一样,XDR对象也支持 timeout 属性以及 ontimeout 事件处理程序。下面是一个例子:

<span style="white-space:pre">	</span>var xdr = new XDomainRequest();
	xdr.onload = function(){
		alert(xdr.responseText);	
	};
	xdr.onerror = function(){
		alert("An error occurred.");	
	};
	xdr.timeout = 1000;
	xdr.ontimeout = function(){
		alert("Request took too long.");	
	};
	xdr.open("get", "http://www.somewhere-else.com/page/");
	xdr.send(null);


 

这个例子会在运行 1 秒钟后超时,并随即调用 ontimeout 事件处理程序。

为支持 POST 请求,XDR 对象提供了 contentType 属性,用来表示发送数据的格式,如下面的例子所示:

var xdr = new XDomainRequest();

xdr.onload = function(){

alert(xdr.responseText);

};

xdr.onerror = function(){

alert("An error occurred.");

};

xdr.open("post", "http://www.somewhere-else.com/page/");

xdr.contentType = "application/x-www-form-urlencoded";

xdr.send("name1=value1 & name2=value2");

这个属性是通过 XDR 对象影响头部信息的唯一方式。

XDR 是 IE 率先在主流浏览器中引入的跨域请求解决方案。虽然要充分利用 XDR 对象还需要开发人员不断地实践,但人们对它的期望则是能够访问 RSS 和 Atom 这样的数据流,以便应用到 Web 开发当中。

17.2.2 跨域 XHR

在 Firefox 3 中,Mozilla 添加了自己的跨域 Ajax 请求解决方案,同样也是基于 W3C 的 "跨站点请求访问控制规范"。最初,这个解决方案是适用于 Web内容的,但发布之后却只能应用于特权脚本和浏览器扩展。不管怎样,人们普遍相信只要有了适当的安全措施,同样的功能最终还会适用于 Web 内容的。本节主要介绍 Firefox 3 中跨域 XHR 的当前状态。

与 IE 的方案类似,W3C 的 "跨站点请求访问控制规范" 要求远程资源有权决定自身是否可以被远程浏览器访问。这就需要通过设置 Access-Control-Aloow-Origin 头部,并指定哪个域可以访问该资源来实现,例如:

Access-Control-Allow-Origin: http://www.wrox.com

这个头部信息指定只有 www.wrox.com 域有访问权限。与 IE 的相同,通过指定 * 值,可以允许所有请求访问当前资源,如下所示:

Access-Control-Allow-Origin: *

如果不是公共 API 或 Web 服务,我们不推荐像这样对所有请求来源开放。

要请求另一个域的资源,可以使用标准的 XHR 对象,并为 open() 方法传入一个绝对 URL ,例如:

var xhr = createXHR();

xhr.onreadystatechange = function(){

if(xhr.readyState == 4){

if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){

alert(xhr.responseText);

}else {

alert("Request was unsuccessful: " + xhr.status);

}

}

};

xhr.open("get", "http://www.somewhere-else.com/page/", true);

xhr.send(null);

与 IE 中的 XDR 对象不同,跨域 XHR 对象允许访问 status 和 statusText 属性,也支持同步请求。不过,为确保安全,跨域 XHR 也存在一些额外的限制,简述如下。

  • 不能使用 setRequestHeader() 设置自定义头部。
  • 不能发送也不会介绍 cookie。
  • getAllResponseHeaders() 方法只能返回空字符串。
由于同域和跨域请求都使用相同的接口,因此最好在访问内部资源时使用相对 URL,而在访问远程资源时使用绝对 URL 。这样不仅可以消除使用中的歧义,还可以避免访问内部资源时对头部信息和 (或) cookie 的操作受限这类的问题。
与 IE 的跨域请求方案类似,Firefox 的实现也远远没有 "物尽其用"。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值