同源策略、跨域、jsonp
1.什么是同源策略
1.要了解同源策略,我们必须先知道源即orgin
- 以百度页面为例,谷歌浏览器打开控制台:输入location.origin可得到百度的源
- 源包括协议版本,域名,端口号。如https://www.baidu.com/,它的协议版本是https,域名为www.baidu,com,默认的端口为443,所以可以省略,类似的还有http协议的默认端口为80,ftp(文本传输协议)的默认端口为21
- 当知道的源以后,就很容易理解同源策略,即只有源相同(域名、协议、端口相同)的客户端脚本才可以读写对方的资源,反之,不同客户端的脚本不能在没有明确授权的情况下读写对方的资源。举例如下:
- 如:http://a.com/a.html 和http://b.com/a.html
二者的域名不相同,不同源,不能读取对方的数据 - 如:https://a.com/b.html 和 http://a.com/b.html
二者协议版本不一样,不同源,不能读写对方的数据 - 如:http://a.com/b.html 和http://a.com:80/b.html
前置省略了默认端口号80,但二者是同源,同时文件路径是否相同和同源无关,所以二者可以读写双方的数据
- 同源的目的:同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。 - 非同源而限制的三种行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 无法获得。
- AJAX 请求不能发送。
- 参考资料:阮一峰的网络日志 -浏览器同源政策
2.什么是跨域?跨域有几种实现形式?
- JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。这里把涉及到跨域的一些问题简单地整理一下:
首先什么是跨域,简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。更详细的说明可以看下表:
- 跨域就是利用某种方法来突破同源策略的限制,实现获取对方的资源的目的
- 跨域的实现方式主要有五种:
- 降域:即双方域名不相同,但同时绑定到某个主域名下,通过设置document.domain+iframe。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,
某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。 domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。 - JSONP:全称为 JSON with padding ,其实质就是动态的创建script标签来实现跨域,它是基于script可以相互引用,即虽然浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function(包括操作cookie、Dom等等)。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信。
3.CORS:Cross-origin resource sharing(全称是"跨域资源共享"),它允许它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 - 使用HTML5 postMessage:HTML5中最酷的新功能之一就是 跨文档消息传输Cross Document Messaging。下一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。 Facebook已经使用了这个功能,用postMessage支持基于web的实时消息传递。
- 其他:利用iframe和location.hash和window.name实现的跨域数据传输等
- 参考资料:JavaScript跨域总结与解决办法
3.jsonp 的原理是什么?
就是利用<script>标签没有跨域限制的“漏洞”(历史遗迹啊)来达到与第三方通讯的目的。当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,形如: <script src="http://www.example.net/api?param1=1¶m2=2"></script>
并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。 第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如: callback({"name":"hax","gender":"Male"})
这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。
- JSONP的安全隐患
(1)任意网站只要通过jsonp方式就可以跨域访问目标域名下的信息,解决办法:在跨域请求数据时在参数中加上与目标域名约定好的一个token变量,这样其他网站访问该域名时,目的网站通过辨认这个约定好的信息而决定是否可以被跨域访问。
(2)不能用post方法获取数据,由于基于src地址引用方式,在地址中附带参数信息,因此只能用get方式获取信息
(3)callback方法由于是根据用户需求自己实现的,可能会被恶意注入脚本,获取隐私信息。 - 参考资料:知乎-JSONP 的工作原理是什么?
4.CORS是什么
- CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。
- CORS 的兼容性,CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
- CORS 的请求分为两类:简单请求(simple request)和非简单请求(not-so-simple request)。以简单请求为例
- 满足以下条件就属于简单请求:
1.请求方法为:HEAD,GET,POST
2.HTTP的头部信息为以下字段:
Accept / Accept-Language / Content-Language / Last-Event-ID/Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain) - 凡是不同时满足上面两个条件,就属于非简单请求。
- 简单请求的基本流程
1.当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头: Origin
2.后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头: Access-Control-Allow-Origin ;
- 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据
-
例子:
假设我们页面或者应用已在 http://www.test1.com 上了,而我们打算从 http://www.test2.com 请求提取数据。一般情况下,如果我们直接使用 AJAX 来请求将会失败。利用 CORS,http://www.test2.com 只需添加一个标头,就可以允许来自 http://www.test1.com 的请求,下图是我在PHP中的 hander() 设置,“”号表示允许任何域向我们的服务端提交请求:
header("Access-Control-Allow-Origin:"); //*表示允许任何域向我们的服务端提交需求header("Access-Control-Allow-Origin:http://www.test1.com") //这样就允许来自http://www.test1.com的需求了
通过在HTTP Header中加入扩展字段,服务器在相应网页头部加入字段表示允许访问的domain和HTTP method客户端检查自己的域是否在允许列表中,决定是否处理响应。
服务器端在HTTP的响应头中加入(页面层次的控制模式):
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range Access-Control-Max-Age: 3600
多个域名之间用逗号分隔,表示对所示域名提供跨域访问权限。”*”表示允许所有域名的跨域访问。
练习
1.本地搭建服务器,演示同源策略
一. 本地搭建服务器(如果使用 SAE 可创建不同的代码版本,这样可通过
1.xxx.sinapp.com和2.xxx.sinapp.com 访问了)
2. 修改 本地host,通过不同域名访问本地服务器。比如访问http://a.com/index.html, http://b.com/ajax.php,本质是
3. 在 index.html 里使用 ajax 接口访问 http://b.com/ajax.php 里的数据。
4. 查看输出报错
- 卡在这一关很久了,因为一直不懂,现在又点懂了,老师视频上用的node.js不会,所用用了本地PHP软件测试
- 1.第一步,打开host 文件,直接可在 win+r 在运行了,输入它的地址 Windows/System32/drivers/etc ,即可找到,如图;
![FUD_4]4@(373R({WX}6AM2G.png](http:https://img.haomeiwen.com/i3361706/0ed330d1d707587b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
2.直接用编辑器或记事本打开即可,若是发现host里面是空白,那是因为杀毒软件360,qq管家刚刚修复过,所以是空的,里面加上这段代码就行了
# Copyright (c) 1993-1999 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a '#' symbol. # # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host
-
第三步,给本地默认IP 127.0.0.1 绑定 两个域名,用于测试
1.png.png -
第四步,在www.a.com/index.html ,通过ajax给www.b.com/test-1.php 发送请求
以下是index.html 的代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <script src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.js"></script> <script type="text/javascript"> $.ajax({ url:'http://www.b.wang.com/test-1.php', type:'get', dataType:'json', data:{ username:'xiaoming', sex:'man' }, success:function(data){ alert(data) }, error:function(){ alert('出错') } }) </script> </body> </html>
以下是 www.b.wang.com的test-1.php的代码
<?php
$data='获取数据成功';
echo json_encode($data);
?>
-
第五步,查看结果,分析原因,如图:
2.png
ajax请求发送失败,查看控制台显示:
F}C[HBV8QG(OGH{N]DVBO8C.png
2.解决同源策略的限制
1.CORS,未来跨域方法的趋势,使用ajax十分简单,安全性高,但是兼容不好,至少要IE10以上,但是随着时代进步,早晚是CORS的天下。
-
具体方式:在www.b.wang.com/test-1.php中添
header("Access-Control-Allow-Origin: http://www.a.wang.com")
;
即允许来自源www.a.wang.com的请求;<?php header("Access-Control-Allow- Origin: http://www.a.wang.com"); $data='获取数据成功'; echo json_encode($data); ?>
-
结果成功的获取到www.b.wang.com下的test-1.php的数据,如图:
-
jsonp,利用动态的script便签的创建获取到数据
index.html的代码如下:
<script type="text/javascript">
function jsonp(data){
alert(data)
}
var script=document.createElement('script');
script.src='http://www.b.wang.com/test-1.php?callback=abc';
document.body.insertBefore(script,document.body.firstChild);</script>
test-1.php 代码如下:
jsonp('123456789');
- 结果如图:成功获取到了数据: