要说清楚JSONP首先得说Same-origin policy,同源策略。为了安全起见,两个不同得网站之间是不能访问对方的数据的,比如A站的cookies不能被B站访问。但是如果这两个网站是相同domain的,比如是同一个公司的网站,那彼此访问是没有问题的,因为是可信任的。就像同domain的OOS(单点登录)的实现可以用cookie来实现一样。
这样的domain,或者说Same-origin里面的origin指的是 URI
scheme, hostname,
and port number,比如http://和https://是不同的domain,协议不同,server.example.com和www.example.com是不同的domain,必须完全匹配,www.example.com和www.example.com:8080是不同的domain,端口不同。
毋庸置疑,所有的现代浏览器都已经实现了这一原则。一个网页可以自由的嵌入来自其他domain的图片,脚本,视频(html5),flash等(一般理解为带src的标签),但是不能使用AJAX请求去向其他domain请求数据,试想如果你可以在你的网站中调用一个网银的接口,改变他们的数据,那是多么不安全。
但是有的时候就是有这种需求,需要跨域请求数据,比如你想要使用google的一些公共服务,google map API,或者需要实现跨域的OOS。在不能向对方直接请求数据的时候怎么办呢?曾经有这么一个牛人他就想到了用javascript,因为跨域的javascript是可以被嵌入到你的页面的,但是这个javascript必须在他自己的domain完成数据的请求再把结果返回给你的domain,所以他返回给你的只能是某种数据格式--JSON
试想如下的script会返回一个JSON:
<script type="application/javascript" src="http://server.example.com/Users/1234"> </script>
{ "Name": "Foo", "Id": 1234, "Rank": 7 }
通过这个script成功的从另外一个domain获得了那个domain里面一个用户的信息,但是这样显然是不够的,把一个JSON片段放进一个html页面中,没有任何javascript变量来接收它是不行的,浏览器会报错。
<script type="application/javascript" src="http://server.example.com/Users/1234?callback=parseResponse"> </script>
客户端必须提供一个回调方法来接收这些数据,于是JSON+padding就被叫做JSONP了。它实现了一种跨域共享数据的方式。
比如baidu地图API有个方法可以返回某个地方的坐标是多少:
bdary.get("四川省成都市天府新区")
显然这个计算坐标的过程发生再百度的服务器而不可能是你自己的服务器,因为你没有相关的方法计算。那百度返回的就是一个JSON的数据,所以你需要提供一个回调方法:
bdary.get("<span style="font-family: Arial, Helvetica, sans-serif;">四川省成都市天府新区</span><span style="font-family: Arial, Helvetica, sans-serif;">", function(rs){ </span> //回调方法,根据返回的数据实现你自己的业务逻辑 });
一个跨域OOS的设想,比如我有个网站:www.example.com,我想要实现和csdn账号的单点登录,也就是说只要用户在当前浏览器登录了csdn就不用再登录,直接可以使用我的网站,并且用的就是csdn的账号信息。当然这得csdn愿意,它如果愿意得话它会提供一个JSONP的调用给我,我调用它的方法之后就可以得到当前的登录状态以及已经登录的用户的信息。
JSONP是有安全隐患的,这很正常,它并不是一个行业标准,只是人们为了解决跨域访问的问题而自己想出的办法。曾经有人提议定义标准来弥补这一缺陷,但是提议被放弃了,因为当时有了跨域访问的另外一个标准CORS。
所以JSONP并不是跨域访问的唯一方法,也不是一个完美的方式,但是它的确是简单好用的方式,并且安全隐患是可以编码解决的。
其他一些解决跨域数据访问的方式参见维基百科:
https://en.wikipedia.org/wiki/Same-origin_policy
本文的一些概念性论述也参考了维基百科。