概念
1、什么是同源策略?
-
URL(Uniform Resource Locator)
统一资源定位符,俗称网址
什么是源(origin)
指页面的协议、域名、端口号;
-
通过
location.origin
可以查看当前页面的源(IE不支持,可以分开写location.protocol;location.hostname;location.port
)
什么是同源策略(Same Origin Policy)
是浏览器的一个功能;
同源就是指协议、域名、端口号相同;
不同源的客户端脚本在没明确授权的情况下,不能读写对方的资源;
-
什么不是同源策略
上图这种跨站引用资源不受同源策略的限制,这算是浏览器的一个妥协(让你下载别的网站的资源来使用),引用过来并没有去读和写它们的内容。我引用过来的资源就在当前的域名了,可以对当前域名下的资源读写,而不能读写原来域名的资源。如果我不是引用而是发起Ajax请求的话便会受到同源策略的限制。
有什么作用
浏览器出于安全方面的考虑,不允许跨域调用其他页面的对象,防止恶意网站盗窃数据。但在安全限制的同时也给注入iframe或是Ajax应用上带来了不少的麻烦,所以有时候就必须跨域。
2、什么是跨域,跨域有几种实现形式?
出于同源策略的限制,不同源的客户端脚本是不能读写对方资源的,而跨域就是突破同源策略的限制,实现跨源通信(协议和端口号不同造成的问题前台是无能为力的)。
-
跨域的实现形式:
(1)、降域[document.domain+iframe]:思路是让不同源的变成同源。- 通过降域可以使两个一级域名相同, 二级域名不同的网页实现跨域资源共享, 但是ajax请求不能通过这种方式跨域, 只有cookie和iframe形式的跨域可以用降域来实现;
- 具体做法是在两个页面文件脚本中都加入
document.domain = "jay.com";
,这样两个页面的js文件就可以互相通信了。
- 缺点:
1、这种方案容易出现大面积的安全问题,只要任何一个子域名被攻击,那么主域名下的信息也会被泄露;
2、只对iframe形式的跨域有效;
3、只对带有同样后缀的域名有效;
- 通过降域可以使两个一级域名相同, 二级域名不同的网页实现跨域资源共享, 但是ajax请求不能通过这种方式跨域, 只有cookie和iframe形式的跨域可以用降域来实现;
(2)、JSONP[JSON with Padding]
- JSONP和JSON没有任何关系,仅仅是因为最后生成的JS文件中的数据一般是JSON格式的,像这样
printData({name: '周花花', age: 20});
因此得名; - 我们知道a.com可以引用b.com的JS文件,那是不是可以在b.com的JS文件里面放数据?
- 实现就是a.com和b.com约定好相同的函数名,a.com中定义这个函数,在b.com中包含这个函数名并且在后面加个括号中间放入数据,即
数据名({json数据});
,但是b.com中的这个东西在a.com看来就是一个函数,它会调用a.com定义的函数。所以在a.com中的函数就被调用并执行,b.com中的数据被当作形参传入a.com定义的函数中。 - JSONP就是服务端动态的生成JS。由于JSONP的服务者要面对很多服务对象,而这些服务对象各自的本地函数都不一样,所以为了让远程的JS知道它应该调用的本地函数叫什么名字,客户端传一个参数告诉服务端我想要一段调用XXX函数的JS代码,请你返回给我,于是服务器就可以按照客户端的需求来生成JS脚本并响应了。
客户端代码:
<!--不建议直接这样引用,我们可以将动态生成script标签封装成一个函数,每次使用的时候直接调用函数-->
<script src="//www.zhouhuahua.com/data.js?callback=onPrintData"></script>
<script>
//www.zhoupenghui.com请求www.zhouhuahua.com下面的数据
//首先定义一个函数,服务端生成的JS会调用并执行这个函数
function onPrintData(data){
console.log(data);
}
//封装一个生成script标签的函数
function printData(){
var script = document.createElement('script');
script.src = "//www.zhouhuahua.com/data.js?callback=onPrintData";
document.body.appendChild(script);
}
printData();
</script>
<!--用jquery实现jsonp调用-->
<script>
$(function(){
$.ajax({
type: "get",
url: "//www.zhouhuahua.com/data.js?callback=printData",
dataType: "jsonp",
jsonp: "callback",
jsonpCallback: "printData",
success: function(data){
console.log(data);
},
error: function(){
alert('fail');
}
});
});
</script>
服务器端生成的JS文件:
onPrintData({
name: '周花花',
age: 22
});
- 缺点:
1、安全性问题,所有的网页都能拿到data.js里面的内容,所以需要校验身份。
2、因为是基于script所以无法触发post请求,只能获取不能写。
3、可能被注入( callback=alert(something); )。
(3)、CORS[Cross-Origin Resource Sharing跨域资源共享]
- b.com声明:我允许a.com来访问我,在响应头添加一个
"Access-Control-Allow-Origin:http://www.zhoupenghui.com"
即可; - a.com中的js发起对b.com的ajax
客户端正常发起ajax请求:
<script>
document.querySelector("#btn").addEventListener('click', function(){
$.ajax({
url: '//www.zhouhuahua.com/CORS.php', //接口地址即请求地址
type: 'get', // 请求类型, post 或者 get,
data: { //发送给后台的数据
username: document.querySelector('#username').value
},
success: function(jsonData){ //响应成功(得到的数据可以是对的也可以是不对的,总之会有一个响应)执行这个函数
var jsonObject = JSON.parse(jsonData); //将后台传的字符串转换为JSON对象
dealwith(jsonObject); //这里的处理函数和上面的一样
},
error: function(){ //失败(没任何响应,比如服务器宕机了啥的)执行这个函数
console.log('出错了')
}
});
});
function dealwith(userInfo){ //对解析出来的JSON对象操作
//我们可以用从服务器拿到的数据,返回的数据是由后台给的字符串,所以我们还要写php
var str = '<dt>性别:</dt>';
str += '<dd>'+userInfo.sex+'</dd>';
str += '<dt>年龄:</dt>';
str += '<dd>'+userInfo.age+'</dd>';
document.querySelector('#ct').innerHTML = str;
}
</script>
服务端只要加一个响应头内容:
<?php
header("Access-Control-Allow-Origin:http://www.zhoupenghui.com");
header('Content-Type:text/html;charset=utf-8');
$usename = $_GET['username'];
if($usename === 'kevin'){
$ret = array('sex'=>'男', 'age'=>23);
}else if($usename === 'david'){
$ret = array('sex'=>'男', 'age'=>30);
}else{
$ret = array('sex'=>'女', 'age'=>18);
}
echo json_encode($ret);
- 优缺点:比JSONP强大,支持所有的http请求,JSONP的优势在于支持老式的浏览器以及可以向不支持CORS的网站请求数据。
(4)、HTML5 postMessage
- 这是HTML5的一个功能
语法为:otherWindow.postMessage(message, targetOrigin);
- 在a.com中用
iframe.contentWindow.postMessage('message',targetOrigin )
给b.com发送消息,在b.com中给window绑定message事件,如果message触发我先判断是不是a.com发来的数据。
a.com中代码:
<iframe id="ifr" src="//www.zhouhuahua.com/postMess.html" frameborder="1"></iframe>
<script>
window.onload = function(){
var ifr = document.getElementById("ifr");
var targetOrigin = 'http://www.zhouhuahua.com';
ifr.contentWindow.postMessage('我是来自zhoupenghui.com的数据', targetOrigin);
}
</script>
b.com中代码:
<script>
window.addEventListener('message', function(event){
if(event.origin === 'http://www.zhoupenghui.com'){
console.log('from:'+event.origin);
console.log('zhoupenghui传来的数据:'+event.data);
}
}, false);
</script>
(5)、其他hack
- hash
- window.name
3、jsonp的原理是什么?
- 不同源的页面数据不能互相读写,但是可以通过script标签引用;
- 在被引用的对象中存放数据;
- 动态引用的同时,提供一个回调函数来接收数据;
- JSONP的本质就是动态的添加script标签来调用服务器提供的JS脚本。
2、CROS是什么?
- CORS是一个W3C标准,它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服ajax只能同源使用的限制。
-
CORS需要浏览器和服务器同时支持,目前所有的浏览器都支持该功能,IE浏览器不能低于IE10。
- 当我们读取另一个站点的资源时,支持CORS的浏览器实际上就已经帮我们发起跨站请求了,只是我们没有得到对方网站的允许,所以浏览器将返回的结果拦截,告诉你不能跨域请求资源。只有当对方允许你读取之后,浏览器才会返回你想要的信息。
练习
1、在本地搭建服务器,演示同源策略
-
本地搭建服务器,多站点配置:
-
修改本地hosts文件,让多个不同域名映射到本地服务器:
-
www.zhoupenghui.com向www.zhouhuahua.com发送ajax请求失败,就是受浏览器同源策略的限制。