二、跨域访问基本原理
在上一篇,介绍了盗链的基本原理和防盗链的解决方案。这里更深入分析一下跨域访问。先看看跨域访问的相关原理:跨网站指令码。维基上面给出了跨站访问的危害性。从这里可以整理出跨站访问的定义:JS脚本在浏览器端发起的请求其他域(名)下的网站数据的HTTP请求。
这里要与referer区分开,referer是浏览器的行为,所有浏览器发出的请求都不会存在安全风险。而由网页加载的脚本发起请求则会不可控,甚至可以截获用户数据传输到其他站点。referer方式拉取其他网站的数据也是跨域,但是这个是由浏览器请求整个资源,资源请求到后,客户端的脚本并不能操纵这份数据,只能用来呈现。但是很多时候,我们都需要发起请求到其他站点动态获取数据,并将获取到底数据进行进一步的处理,这也就是跨域访问的需求。
现在从技术上有几个方案去解决这个问题。
1、JSONP跨域访问
利用浏览器的Referer方式加载脚本到客户端的方式。以:
1 |
<script type="text/javascript" src="http://api.com/jsexample.js"></script> |
这种方式获取并加载其他站点的JS脚本是被允许的,加载过来的脚本中如果有定义的函数或者接口,可以在本地使用,这也是我们用得最多的脚本加载方式。但是这个加载到本地脚本是不能被修改和处理的,只能是引用。
而跨域访问需要正是访问远端抓取到的数据。那么能否反过来,本地写好一个数据处理函数,让请求服务端帮助完成调用过程?JS脚本允许这样。
1 2 3 4 5 6 7 |
<script type="text/javascript"> var localHandler = function(data) { alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result); }; </script> <script type="text/javascript" src="http://remoteserver.com/remote.js"></script> |
远端的服务器上面定义的remote.js是这样的:
1 |
localHandler({"result":"我是远程js带来的数据"}); |
上面首先在本地定义了一个函数localHandler,然后远端返回的JS的内容是调用这个函数,返回到浏览器端执行。同时在JS内容中将客户端需要的数据返回,这样数据就被传输到了浏览器端,浏览器端只需要修改处理方法即可。这里有一些限制:1、客户端脚本和服务端需要一些配合;2、调用的数据必须是json格式的,否则客户端脚本无法处理;3、只能给被引用的服务端网址发送get请求。
1 2 3 4 5 6 7 |
<script type="text/javascript"> var localHandler = function(data) { alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result); }; </script> <script type="text/javascript" src="http://remoteserver.com/remote.php?callBack=localHandler"></script> |
服务端的PHP函数可能是这样的:
1 2 3 4 5 6 7 8 |
<?php $data = "......."; $callback = $_GET['callback']; echo $callback.'('.json_encode($data).')'; exit; ?> |
这样即可根据客户端指定的回调拼装调用过程。
2、CORS(Cross-origin resource sharing)跨域访问
上述的JSONP由于有诸多限制,已经无法满足各种灵活的跨域访问请求。现在浏览器支持一种新的跨域访问机制,基于服务端控制访问权限的方式。简而言之,浏览器不再一味禁止跨域访问,而是需要检查目的站点返回的消息的头域,要检查该响应是否允许当前站点访问。通过HTTP头域的方式来通知浏览器:
1 2 3 4 5 6 7 |
Response headers[edit] Access-Control-Allow-Origin Access-Control-Allow-Credentials Access-Control-Expose-Headers Access-Control-Max-Age Access-Control-Allow-Methods Access-Control-Allow-Headers |
服务端利用这几个HTTP头域通知浏览器该资源的访问权限信息。在访问资源前,浏览器会先发出OPTIONS请求,获取这些权限信息,并比对当前站点的脚本是否有权限,然后再将实际的脚本的数据请求发出。发现权限不允许,则不会发出请求。逻辑流程图为:
浏览器也可以直接将GET请求发出,数据和权限同时到达浏览器端,但是数据是否交给脚本处理需要浏览器检查权限对比后作出决定。
一次具体的跨域访问的流程为:
因此权限控制交给了服务端,服务端一般也会提供对资源的CORS的配置。
跨域访问还有其他几种方式:本站服务端代理、跨子域时使用修改域标识等方法,但是应用场景的限制更多。目前绝大多数的跨域访问都由JSONP和CORS这两类方式组成。