欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jsonp跨域到底是怎么一回事?

程序员文章站 2022-07-14 12:14:58
...

什么是跨域

1.浏览器的同源策略

       在了解浏览器同源策略之前我们要先知道什么是源(origin),源就是协议、域名、端口,所谓的同源也就是协议、域名和端口号均相同。同源策略是浏览器最核心、最基本的安全功能,非同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。一个页面在请求非同源页面的资源时,会被浏览器拒绝。同源策略的限制点主要在针对接口的请求和DOM操作上。
       问题来了,我们有时候不可避免地向不同源的站点请求资源,该怎么办呢?这就是我们常说的跨域,跨域的方法有很多,本文只针对jsonp一种展开。

2.浏览器为什么要有同源策略?

       浏览器的同源策略肯定不是没事找事故意为难开发人员,那么浏览器为什么要有这样一个同源策略呢?这要从同源策略的限制点说起,我们来看看假如没有同源策略的限制,会发生什么危险的情况。

a.我们的浏览器没有限制非同源的接口请求

       假如你想登陆你的网上银行给朋友转账,按照流程输入账号密码登陆成功。这时,你一不小心打开了一个恶意的钓鱼网站。你刚刚登陆的时候cookie是存在本地的,因为没有同源策略的限制,所以这个钓鱼网站暗地里就可以使用你刚刚的cookie向接口发送请求,这就相当于钓鱼网站登陆了你的网银账户,那后果不堪设想。这也就是所谓的CSRF(跨站请求伪造)攻击,但这在有同源策略防护的情况下是不会轻易发生的。

b.我们的浏览器没有限制访问非同源的DOM

       假如你还是想登陆你的网上银行给朋友转账,你一不小心进入了一个恶意的钓鱼网站,这个网站通过一个iframe引用原网站。你进入的页面表面上看起来跟原网站没有什么区别,你按照流程输入账号密码登陆。这时,因为我们的浏览器没有限制访问非同源的DOM,所以钓鱼网站只需要简单的getElementById(input)就可以轻易地得到你的账号密码,然后为所欲为。同样,这在有同源策略防护的情况下也是不会轻易发生的。
       由此可见,同源策略确实能规避一些危险。但不是说有了同源策略的保护,我们访问页面就可以高枕无忧了,只能说同源策略是浏览器给我们提供的最基本的防护机制,能一定程度上降低被攻破的可能。

什么是jsonp?

       上文提到了跨域的方法,jsonp是其中常见的一种。我们知道拥有src属性的标签都不受同源策略的限制,如</script>、</img>、</iframe>等,jsonp就是利用这个原理逐渐形成的一种非正式传输协议。
       我们都知道json数据格式可以简单地描述复杂数据,而且json被原生js完美支持(可以与js对象任意互相转换),那么如果服务端动态地将客户端需要的json格式的数据包装起来,客户端通过标签引入对应的js文件就可以获得需要的数据了。jsonp的核心过程就是用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住json数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
       这样看起来可能还是有点抽象,我们接下来直接看代码,“jsonp协议”到底是怎么做的。

jsonp具体是怎么做的?

       jsonp通过</script>的src属性,将远程的js文件引入并执行,也就是将一个js文件加到当前页面的里执行。
1.假如我们已知abc.com目录下有一个abc.js文件如下:

console.log("remote file");

def.com目录下有一个def.html页面如下:

<!DOCTYPE>
<html>
<head>
	<script type="text/javascript" src="http://abc.com/abc.js"></script>
</head>
<body>
</body>
</html>

       运行结果是def页面控制台输出remote file,这是最简单的jsonp跨域demo。

2.我们在def页面定义一个函数,在远程js文件里调用,然后引入远程js文件。abc.js文件如下:

a({"info":" jsonp跨域"});

def.html如下:

<!DOCTYPE>
<html>
<head>
	<script type="text/javascript">
		var a=function(data){
			console.log("远程js文件的数据是"+data.info);
		};
	</script>
	<script type="text/javascript" src="http://abc.com/abc.js"></script>
</head>
<body>
</body>
</html>

       很显然,def页面的控制台会输出远程js文件的数据是jsonp跨域。那么问题来了,我们怎么让远程js文件知道该调用哪个函数呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。
3.我们可能会想到,只要服务端的js文件是动态生成的就可以了呗,客户端可以传过去一个参数告诉服务端我想要一份“你来调用某个函数的js代码”,这样服务端就能响应客户端的要求返回需要的js代码了。
def.html如下:

<!DOCTYPE>
<html>
<head>
	<script type="text/javascript">
		var a=function(data){
			console.log("远程js文件的数据是"+data.info);
		};
		//定义js文件的地址
		var url="http://abc.com/abc.js?need=data&callback=a";
		//创建script标签并设置地址
		var script=document.createElement("script");
		script.setAttribute("src",url);
		//把script标签加入head
		document.getElementByTagName("head")[0].appendChild(script);
	</script>
</head>
<body>
</body>
</html>

       这样一来,我们看到url中有两个参数,need告诉服务端我请求的数据是data,callback告诉服务端我的回调函数是a,请你把need作为参数调用该函数。那么服务器将返回如下js代码:

a({"info": "jsonp跨域"});

ps:服务端的操作就是拼接一个字符串。


       现在就已经理解jsonp的原理了吧,剩下的就是处理获得的数据用于渲染或者交互啦。但本质上来说jsonp只能用于get方法不能用于post。




       以上是我对jsonp的理解,如有错误还请大牛指正,如有不同意见也欢迎质疑,希望对大家有所帮助,也希望同样在准备春招的朋友一切顺利。