Quantcast
Channel: 涂0实验室 » web
Viewing all articles
Browse latest Browse all 2

Javascript跨站点访问

0
0

如何让javascript访问一个和自己所在网站的域名不一样网站?

什么是SOP

在浏览器的设计上有一个SOP (Same Origin Policy),就是相同源政策,是指浏览器只允许javascript访问与当前页面有相同源服务器的资源。这里所谓的同源服务器,是指协议端口和域名都一样。这一政策的目的是为了让浏览器更安全。下面一个例子演示了SOP。为了测试方便,通过修改hosts文件把 test1.com test2.com都指向本机。在apache的htdocs下放以下程序。

sop.php

<html>
	<head>
		<script src="js/jquery.js" type="text/javascript"></script>
	</head>
	<body>
		<div id="container"></div>
		<script type="text/javascript">
			$(document).ready(function(){
				$.get(
					"http://test2.com/data.txt",
					function(data){
						$("#container").html(data);
					}
				);
			});
		</script>
	</body>
</html>

data.txt

test2.com data

通过浏览器访问 http://test1.com/sop.php,如果没有sop的话,那么应该能在页面里看到test2.com data字样,因为这个程序里面用ajax读取http://test2.com/data.txt的内容放到了id是container的元素内。但是事实上在ie, firefox, chrome几个浏览器下面均看到不到,而且在chrome下面,还会有明确的出错提示:

XMLHttpRequest cannot load http://test2.com/data.txt. Origin http://test1.com is not allowed by Access-Control-Allow-Origin.

如何突破SOP

如果真的需要在当前页面里面显示和当前页面非同源的资源,那该怎么办呢?有以下几种方案。

服务端集成

在服务器上,用程序读取远程信息,然而生成到页面中。困为是在服务端,所以当然不会用浏览器的SOP限制了。但是这样一来会增加服务端的集成难度,没有在客户端集成灵活,生成页面的响应时间也更长,因为用户必须等到服务端接收并处理完非同源的资源后,才能收到页面。

服务端代理

这个方法就是建一个同源的反向代理服务器,把非同源的资源转化成了同源的。这样做不违反SOP,也能做客户端集成了,但是要搭建专用的服务器,而且比直接访问资源更慢,当然你可以通过增加缓存来提高性能。

采用JSONP

JSONP利用了script元素的一个例外来实现的。尽管你不能在javascript程序里面直接访问非同源资源,但是却可以通过script载入非同源的资源,比如:

<script type="text/javascript" src="http://test2.com/jsonpdata.php" ></script>

就算这个段代码所在的页面是test1.com,也能载入这个js。但是很多时候,我们是要数据,而不是要javascript程序,如果http://test2.com/jsonpdata.php只返回一段json的话,那么在当前页面我们并没有办法使用它。怎么办呢?前人想到的办法是在请求json的时候约定一个函数名,返回的json数据直接被个函数调用。看下面的例子,就明白了。

jsonp.php

<html>
	<head></head>
	<body>
		<div id="container"></div>
		<script type="text/javascript">
			function myfunc(data) {
				document.getElementById("container").innerHTML = data;
			}
			var remoteUrl = "http://test2.com/jsonpdata.php?callback=myfunc";
			var scriptElement = document.createElement("script");
			scriptElement.type = "text/javascript";
			scriptElement.src = remoteUrl;
			document.body.appendChild(scriptElement);
		</script>
	</body>
</html>

jsonpdata.php

<?php echo $_REQUEST['callback'] ?>('test2.com data');

上面的例子中,

  1. 先设定好回调函数myfunc
  2. 再组装一个script元素,并在src地址后面加上callback=myfunc
  3. 把这个元素放到dom里面,不论是不是同源,浏览器都会自动地载入src所指的javascript
  4. jsonpdata.php生成的代码是 myfunc('test2.com data'); 
  5. 浏览器立即执行这段javascript,前面定义的好的myfunc就被调用了

这个方法巧妙地绕过了SOP,但是要求服务端必须支持jsonp,即在生成的数据前面加上回调函数的名字。如果非同源的资源并不支持jsonp,那这招就没用了。

CORS

jsonp有些旁门左道的感觉,于是有人针对性地提出对http协议的改进,就是Cross Origin Resource Sharing。这个要求服务器和客户端都支持才行。客户端在请求非同源资源时在HTTP头部加上Origin: test1.com类似的信息,然后非同源服务器返回的头部带上诸如Access-Control-Allow-Origin样的头部,来指明是否可以共享。这个好像比较复杂,我在本地测试了一下,chrome 12.0.742.100 好像还是有问题,明明我已经在Response头部加上了 Access-Control-Allow-Origin: test1.com,但是还是没有成功。看来这个技术还有待于被各家浏览器接受和实现。更多信息参看 http://www.w3.org/TR/cors/

利用iframe

这个没有违返SOP,实现了在当前页面里显示非同源的资源,尽管事实上不是在一个frame里面,也没有用javascript跨源访问,但是看起来像是在同一个页面就行了。google adsense就是这么做的,可见这个技术是成熟可靠的。具体怎么做,就不说了。

总结

如果可以的话,jsonp是最简单灵活方便的,cors还需要时间成长,iframe用来处理单纯地嵌入页面展示最方便了,但也要非同源资源处理好展示效果。实在不行就在server端集成,或者搞个代理算了。


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images