什么是跨域
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的理解,如有错误还请大牛指正,如有不同意见也欢迎质疑,希望对大家有所帮助,也希望同样在准备春招的朋友一切顺利。