今天终于梳理清楚了AJAX跨站请求的原理和解决方案,在此记录下,作为备忘。
浏览器同源策略
同源策略又名同域策略是浏览器中的主要安全措施。这里的“源”指的是主机名、协议和端口号的组合;我们可以把一个“源”看作是某个web页面或浏览器所浏览的信息的创建者。 同源策略,简单地说就是要求动态内容(例如,JavaScript或者VBScript)只能阅读与之同源的那些HTTP应答和cookies,而不能阅读来自不同源的内容。更为有趣的是,同源策略对写操作没有任何限制。因而,一个web站点可以向任何其他的Web站点发送(或写入)HTTP请求,尽管为 了防止跨站请求可能会对发送这些请求有关的cookies和头部有所限制。(以上描述来之网络)
通俗的讲:同源策略就浏览器不允许是站点A的js脚本访问站点B提供的动态数据,因为这样可能带来安全隐患。
如何跨域
跨域就是绕过同源策略限制,使得站点A的js脚本可以访问到站点B提供的数据。怎么绕过呢?比较常见跨域方案是使用代理,即在A站点下搭建一个代理(可以是个自己的CGI),让代理访问B站点,并将结果反给A页面,这种方法的可扩展性不够好,实施成本大。目前比较经典的方案使用JSONP方式(json with padding)。从上面的定义中,可以知道浏览器不允许A的js脚本访问B提供的数据,但是没有阻止拉取B站点提供的静态资源,如js文件,图片等。所以,跨域的关键在于如何利用好这些“静态”的内容。举个例子:
A站点需要访问B站点的数据,B站点有下面的脚本(PHP编写的cgi):
<?php echo json_encode(array(‘msg’=>’cross domain access’)); ?>这样是无法拿到数据的,因为B站点吐出的是json数据,如下:
{“msg”:”cross domain access”}但如果将B站点的脚本改成下面的样子,站点A就可以访问到数据。
<?php echo ‘localFunc(’. json_encode(array(‘msg’=>’cross domain access’)). ‘);’; ?>因为此时,返回的结果如下:
localFunc({“msg”:”cross domain access”});没错,是一个函数调用,可以理解为脚本,而不是数据,浏览器认为合法,所以允许访问。当然,localFunc函数需要在A站点的页面定义。
script标签跨域
上面描述了原理,接下来描述具体如何实时。首先,我们需要搭建两个站点,我的实验环境是LAMP,所以在apache中设置了两个virtual host: 和。
搭建好后,将文件index.html拷贝到站点A根目录
<!DOCTYPE html> <html> <!--A站点首页 index.html--> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.1.min.js "></script> <title>cross domain demo</title> </head> <body> <input type="button" value="JSONP跨站原理"></input><br/> <input type="button" value="JSONP跨站jquery简化后版本"></input><br/> <input type="button" value="getScript跨站"></input><br/> </body> <script> function localFunc(data) { alert(data.msg); } function onClickCrossJSONPTheory() { var script = document.createElement("script"); script.type = "text/javascript"; script.src = "http://b.oa.com/index.php?callback=localFunc"; document.getElementsByTagName(\'head\')[0].appendChild(script); // 省略删除 } function onClickCrossJSONP() { $.getJSON(\'http://b.oa.com/index.php?callback=?\', function(json) { alert(json.msg); }); } function onClickJQueryScript() { $.getScript(\'http://b.oa.com/alert.js\', function(){ cross(); cross(); }); } </script> </html>将index.php文件拷贝到B站点根目录
<?php // B站点首页: index.php echo $_GET[\'callback\'] . \'(\' . json_encode(array(\'msg\'=>\'hello cross-domain\')) . \');\'; ?>为A站点和B站点配上host(很重要),打开浏览器,输入“”,得到如下页面:
点击第一个按纽会弹出对话框,答应站点B的内容,如下:
我们看看index.html中都做了些什么:
1. 定义回调函数localFunc
2. 在onClickCrossJSONPTheory函数中动态加载script标签
3. 将localFunc作为url参数
4. 清理创建的标签(省略),如果不清理,script标签会越来越多。
而B站点将传过来的回调函数包装上(padding)自己的数据(JSON),返回给A站点。
这么简单,就“骗”过了浏览器,不是吗?
$.getJSON跨域