跨域----jsonp

为什么要跨域

由于浏览器的 同源策略,当我们违背浏览器的同源策略,就行跨域操作的时候,浏览器默认会禁止你干这种‘违法’的事情。

同源策略的重要性

同源策略是浏览器安全的基石,没有同源策略…后果真的不可想象.

嗯?为啥子?且听老夫慢慢道来

假设一个场景:

现在你应该可以猜到浏览器的同源策略到底是干嘛用的了.

1995年,同源策略由NetScape公司引入,到现在的所有浏览器遵守同源策略.其中的含义也发生了一些改变,暂且不提.

简单的说,同源策略中的同源的含义是要有”三个相同”:

  1. 协议相同
  2. 域名相同
  3. 端口相同

举例说明一下

假设当前域名是https://raoul1996.github.io PS:在这个例子中,io是顶级域名,github是一级域名,raoul是二级域名

http://raoul1996.github.io              //不同协议
https://raoul1996.github.io:8080        //不同端口
https://test.github.io                  //不同域名
https://raoul1996.github.io/index.html  //同源,可以访问到资源

不知道客官是否懂了呢?

跨域场景

再次假设一个场景: 比如,现在你要做一个在线的天气查询页面,肯定是拉取别的域名下的数据,对吧。这就是跨域最常见的作用。

跨域的方式

目前主流的跨域方式有以下几种:

  1. jsonp
  2. cors
  3. 降域
  4. postMassage

当然使用别的方式也不是不可以。比如在后端写一个代理什么的。这个我也不咋会。

跨域必须要前后端协作,仅仅靠前端自己是完不成的。

今天我们不讲cors,我们不讲postMassage,我们也不说降域,我们聊一聊jsonp。

jsonp的原理

先来几个问题压压惊

问题1:

在我们初学jquery的时候,我们被推荐从2-3个不同的CDN资源库去加载jquery.js,那么,这算不算跨域?

答案是,不算。

浏览器允许我们加载来自别的域名下的js。其实个人觉得说下载更合适。将目标的js文件的内容下载到当前的页面的一对script标签中。

问题2:

假设index.html页面在http://a.com下,a.js和b.php在http://b.com下。index.html中加载了a.js,然后a.js调用了http://a.com下的a.php中的数据,算不算跨域?调用了http://b.com下的b.php中的数据,这次算不算跨域?

答案是前者不是跨域,后者是跨域。

从不同的域名上加载JS文件,是浏览器所允许的。看是不是跨域看的是html文件的域,而不是JS文件的域。

各位明白了不?

因为浏览器允许加载不同域名下的js,那么再想。我们加载个txt文件行不行?加载一段数据行不行?

当然可以,上文已经提过,通过定义script标签的scr属性,将目标的文件的内容下载到当前的页面的一对script标签中。不管是不是js文件,是txt,php,或者其他格式也不要紧(必须是浏览器能看的懂的)。

然后script标签还有一个特性就是:加载下来立即执行。

如果有一个巧合,我们的在http://a.com下的index.html页面中,有一个名字为test(data)函数,我们从不同域的接口上拉取到了一个字符串,是这个样子的:test(1)。那么可不可以执行test函数,并且把1作为参数传进去呢?

看不懂没关系,我们来个例子:

index.html的内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>jsonp实践</title>
</head>
<body>
<script>
	function getChannel(data) {
	console.log(data);
}
</script>
<script>
	var script =document.createElement('script');
	script.src="http://api.jirengu.com/fm/getChannels.php?callback=getChannel";
    //调用来自jirengu.com下的接口
	document.head.appendChild(script);
	document.head.removeChild(script);
</script>
</body>
</html>

我保证我的index.html不在http://api.jirengu.com下面。这是跨域操作。

http://api.jirengu.com/fm/getChannels.php?callback=getChannel 返回的数据。。是这个样子的:

getChannel({"channels":[{channel_id":"public_yuzhong_yueyu"}]})

加载下来上面这一坨东西后,浏览器立即执行。而且我们的页面预先已经定义了getChannel()方法。就可以调用从jirengu.com获取到的数据了。

jsonp就是这么简单

再附一个用jquery的实现方法:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>jsonp-jquery</title>
	<script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.js"></script>
</head>
<body>
<script>
	$.ajax({
		url: 'http://api.jirengu.com/fm/getChannels.php?callback=getChannel',
		type: 'GET',
		dataType: 'jsonp',
		jsonp:"callback"
	})
	.done(function(data) {
		console.log(data);
	})
	.fail(function() {
		console.log("error");
	})
	.always(function() {
		console.log("complete");
	});

</script>
</body>
</html>

jsonp有什么需要注意的?

  1. jsonp不像XMLHttpRequest对象一样,需要浏览器的支持,它的兼容性更好。
  2. jsonp需要后端对返回的数据进行一下包装。包装成一个函数加参数的样子,这样javascript函数才能执行,一般都是用callback。
  3. jsonp只能进行get请求的跨域,至于post请求还是去看看cors跨域吧。
  4. jsonp使用的是脚本注入的方式,所以会存在一定的安全隐患

啥时候谈谈cors、降域、postmassage方法?

等我考完期末呗。

Table of Contents