天天看点

AJAX跨域数据获得的研究

跨域需求其实由来已久,之前是公司的网站需要同不同域名下的同为公司网站的数据交互,但始终没有做出来,当时时间也无法都花在这个上,所以就不了了之了。至今有技术界的朋友问起相关问题,由考虑后期服务器架构优化网站效率优化与跨域有着必然的联系,于是又捡起这个话题重新研究。

同源策略

说跨域则不得不说同源策略。所谓同源策略是W3C网络标准制定的一个安全方面的限制,就比如说,我们可能之前在哪里看到一个比较漂亮的背景图片,找到它的URL后就可以直接用在自己的网站里,并不一定要下载到自己的网站目录中,例如CSS的代码可以如下书写

background-image:url(http://www.xxx.com/images/123.jpg);

即使此时自己的网站域名同这个图片的加载域名不同,也可以加载这个图片。

但同源策略并不是说这个,而相反是一种限制,尤其是在AJAX技术盛行的WEB2.0的今天,同源策略起到了它应有的保护作用,否则网络安全将会大打折扣。

对于AJAX技术熟悉的朋友,都会知道通过JS前端的事件触发(当然并不一定需要交互触发才能实现AJAX交互)来同服务端进行AJAX的数据交互,从服务端返回的数据可以是普通的HTML,XML也可以是JSON,但一般的交互都是同自己域名下的服务端文件,用相对路径引用居多,如果自己公司有多个域名,网络布局各域名针对的业务功能都不同,但有域名A需要调用域名B下的数据,如果用户通过普通AJAX调用 例如www.test1.com/index.html页面调用 www.test2.com/getData.php下所得到的数据,但实际是无法获得到的,AJAX的返回会提示调用失败的状态。这个即是同源策略在其作用。

同源策略是指,网络请求如何在同一域名下,同一访问端口下,同一请求方案下,才算做是同源,比如

http://www.my.com 和 http://www.my.cn 不是同源,因为域名不同

http://www.my.com 和 http://list.my.com 也看作不是同源,因为一个是顶级域名,一个是二级域名

http://www.my.com:80 和 http://www.my.com:8080 也不是同源,因为所访问的端口不同

http://www.my.com 和 https//www.my.com 也非同源 因为访问的请求方案(协议)不同,一个是http请求,一个是https的请求 

以上都是同源策略限制的访问方式,所以一般情况下,安全策略是禁止你访问非同源下的资源文件的。

明天继续

以上是同源策略的解释,但注意,并非所有互联网资源都受同源策略影响,最简单就是我一开始说的那个图片的例子。 实际还有一些HTML资源也并不受同源策略影响,分别罗列在下面,当然不一定全面。

1 样式表文件

样式表文件作为网站的样式提供者,书写方式可以单独为文件,比如style.css,或者嵌入在html文件中。当然后者不干同源策略什么事,主要说的是前者,单独的style.css样式表文件,实际不受同源策略影响。具体解释为:即使在我自己的网站中使用了新浪网上的样式表文件,样式表依然可以很好的工作,只要我的HTML的结构中有样式表中的定义,自然会作用到页面上而无需担心没有权限访问。

例如 <LINK rel="stylesheet" type="text/css" href="http://news.sina.com.cn/css/87/20110909/218/vweibo.css"> 虽然加载的是外部样式表,但依然可以正常作用。

2 js客户端脚本文件

可能对js特性不太了解的技术人员并不知道可以引用网络资源的js文件,例如

<script language="javascript" src="http://code.jquery.com/jquery-latest.js"></script>

该段引用表示在页面中引用最新版的jQuery框架文件。因不受同源策略影响,故此js文件可以正常加载,浏览器也不会报错。

值得注意的是,加载相关外部js文件后,当前页面的操作权就对这个js开放了,它通过一些dom更改语句可以任意修改你的页面。所以一般在不了解外部js的功能情况下,不建议随意加载外部js。

当然HTML语言中有个比较特殊的东西,就是iframe,我们称为框架的东西,这个可以加载任意的外部页面,所以也不受约束,严格来讲实际不是一马事。当然别以为iframe可以加载网络任意文件你就可以为所欲为,它的限制是,你没有办法对iframe的内容进行任何操作,那怕就是简单的获得他的innerHTML也是不可以的。

以上为同源策略说明和实际的一些规避元素,了解这些基本知识之后,我们就可以去研究如何让ajax跨域加载数据了。

一 使用新版本浏览器的Access-Control-Allow-Origin属性

该属性一般不会用到,做跨域的时候我才接触到。 在做的时候也从网上找了它的相关资料,但百度下并不多,不记得哪个网站上说的,该属性是因为W3C考虑到同源策略规则限制了对于某些确实需要跨域的需求所做的协调。先不管其是否正确,先看看这个属性具体作用。

该属性表示当前页面可允许的外来请求域,例如 php下的head设置代码

<?php

header("Access-Control-Allow-Origin:http://www.test1.com");

?>

设置完之后,该页面就表示可以接受从域www.test1.com发过来的请求,然后该页面就会准备数据然后返回。

需要注意的有几点

1 该属性的书写一般用于被调用页面,所以如果你把该属性写在发起调用的页面,例如www.test1.com/index.php 则无法正常响应,因为被请求页面没有设置。所以更详细来说,如果你希望你自己的网站能够调用比如sina网的某些新闻资讯,通过ajax调用,就必须得在sina的相关页面上去设置这个参数,且加上自己的域名,当然一般不会有sina的页面修改权限,故这个是行不通的,除非用它公开的调用接口。

2 该书写的域名写法记得要加前缀http或者其他方案(协议),看上面我说的如何判定是否同源就知道了。但自己测试的时候加端口却无法正常返回,并不知道是什么原因

3 低版本的浏览器并不支持该属性,因为AJAX的出现也就在这几年(我接触的时候应该是09年,08年在上海的时候还没搞过,不过可能已经有了),我自己测试的时候用的是IE8和firefox最新版,基本IE8下没有办法正常响应,而firefox下则可以。兼容性问题有时会让人比较郁闷。

4 客户端请求似乎对浏览器也有讲究,网上些资料说的是IE8下则需要用另外一个对象进行跨域请求,但firefox下普通的AJAX调用就可以了。

所以总结如下,对于该属性的跨域,在浏览器的使用者五花八门的今天,为了保证兼容性,则需要做大量测试和兼容代码书写的工作。所以其实我并不推荐用这个方法来做跨域,除非以后大家都用上最新的浏览器,兼容性问题逐步减少的时候(实际应该说网络协议与代码越来越规范的时候)采取考虑吧。

把我的测试代码附带如下

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=">

<script language="javascript" src=">

<title>发起文档</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    //这个是自己在jquery的框架ajax调用的基础上书写的js函数,如需测试可自己在jquery框架下自己手写个

    //参数说明 第一个表示请求的URL 第二个POST传递的变量 第三个返回结果的显示在哪个对象中,这里的调用是用html方式调用的

    //第四个 返回后的回调函数

    JQ_CallAJAX("http://www.test2.com/index.php","",$("#msg"),"getReturn()");

});

function getReturn(){

    //书写回调函数的相关代码

}

</script>

</body>

</html>

www.test2.com/index.php

<?php

    header("Access-Control-Allow-Origin:http://www.test1.com");

    echo ("this page is ajax cross get");

?>

然后在本地电脑配置好相关的环境,修改host文件增加www.test1.com和www.test2.com的解析指向,然后在firefox下运行就可以看到相关结果了。

二 使用不受同源策略影响的html元素进行跨域获取

上文说到不受同源策略影响的元素有图片,样式表文件和js客户端脚本文件,这里图片和样式表我们都不需要,唯独用到js脚本文件。

另外一个技术就是服务端文件可以生成发送至客户端的任意文件,比如图片。大家可以看到某些网站上的图片URL并不是.jpg结束,而是.asp或者.php后缀,其实就是有服务端文件生成的,当然例如pdf文件,doc文件在服务端都可以为你生成,js文件也不例外,只要在服务端把contentType属性设置好,然后文件内容也规整好就成了,这个可以看下我之前些的一篇《WEB文档提示下载的研究》,这里就不在详细介绍

先上代码再做解释吧

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=">

<title>发起文档</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    //其中的callback的值为请求被接受后的回调函数名称

    addScriptTag("http://www.test2.com/index.asp?callback=getReturn")

});

//回调返回,其中data为回传的json数据

function getReturn(data){

    if (data){

        alert(data.result);

    }else{

        alert("跨域调用不成功!");

    }

}

//自定义加载js包含标签

function addScriptTag(src){

    var script = document.createElement('script');

    script.setAttribute("type","text/javascript");

    script.src = src;

    document.body.appendChild(script);

}

</script>

</body>

</html>

www.test2.com/index.asp

<%

 response.ContentType="application/json"

 callback=request.QueryString("callback")

 response.write(callback&"({""result"":""ok123""})")

%>

代码说明,其实从test1发起的请求,实际是在body之间生成一个script标签,该标签的href=不同域下的一个文件,虽然这里是index.asp文件,但如上所说,实际该服务端文件生成的是一个js文件,应为有ContentType="application/json"的这个头部定义,所以可以放在script里面进行调用,而调用的这个页面只要把相关的数据准备好就行了。所以之间就不存在同源策略的限制。

另外注意这里有个callback的参数传递,这个是用于返回数据的时候调用test1的相关js函数,否则直接返回json数据将不会被响应。

三 同样使用不受同源策略影响的js,在jquery框架中调用

其实在jquery现在的框架文件中,(我用的是1.71)基本也支持这种方式的“跨域”,实际的调用类型叫JSONP,似乎类似于JSON,但具体怎么解释这个我就不太清楚了。

jquery的调用我也直接是用的ajax方法,只不过该方法(对象)的属性dataType设置为jsonp了,对于被请求页面则不需要做任何修改,于是代码我就罗列一个请求页面的。

www.test1.com/index.html 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns=" http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<script language="javascript" src=" >

<title>发起文档</title>

</head> <body>

<div id="msg"></div>

<div id="go"></div>

<script language="javascript">

$("#go").click(function(){

    $.ajax({

        url:"http://www.test2.com/index.asp?callback=?",

        dataType:"jsonp",    //注意这里必须写jsonp

        jsonpCallback:"getReturn",

        success:function(data){

            alert(data.result);

        }

    });

});

//回调返回,其中data为回传的json数据

function getReturn(data){

    if (data){

        alert(data.result);

    }else{

        alert("跨域调用不成功!");

    }

}

</script>

</body>

</html>

其中ajax中的带啊jsonpCallback为执行后的回调函数,但其中也有ajax自己的success的函数,实际测试下来两个函数都会有相应,且回调函数的执行在success函数执行之前。如果把回调函数的声明去掉,或者把success函数去掉,依然会响应,实际与是否连接到不同域下的文件无关,而是与数据返回过来的处理有关

由于时间有限,没能测试通过该方法到一个实际的js文件,然后该js文件发起ajax请求与这个js同域下的一个服务端文件结果是否可行,当然不可行也不妨碍使用,之际用ajax方法或者第二种都可以。只不过用服务端文件来准备js需要的数据而已,而不是通过不同域下的js再转换。

所以综合上面的叙述,AJAX跨域考虑浏览器兼容性的做法,要么就直接写js来动态添加script标签进行异地js加载,或者直接用jquery框架来实现,两者基本都能很好的执行。

最后参考网站贴一下,对人家劳动的尊重:http://www.cnblogs.com/chopper/archive/2012/03/24/2403945.html

继续阅读