GitHub:http://liu12fei08fei.github.io/html/3domain.htmljavascript
同源策略(跨域的由来)php
同源策略限制从一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。html
“同源”是指:协议相同、域名相同、端口相同前端
同源政策的目的:是为了保证用户信息的安全,防止恶意的网站窃取数据。java
同源的限制范围:jquery
1. Cookie、LocalStorage 和 IndexDB 没法读取git
2. DOM 没法得到github
3. AJAX 请求不能发送web
这里须要明确的一点是:所谓的域跟js的存放服务器没有关系,好比baidu.com的页面加载了google.com的js,那么此js的所在域是baidu.com而不是google.com。也就是说,此时该js能操做baidu.com的页面对象,而不能操做google.com的页面对象。ajax
跨域的方法
1、使用JSONP跨域(单项跨域-通常用于获取数据)
原理:由于经过script标签引入的js是不受同源策略的限制的(正如前文提到的baidu.com的页面加载了google.com的js)。因此咱们能够经过script标签引入一个js或者是一个其余后缀形式(如php,jsp等)的文件,此文件返回一个js函数的调用,如返回一个对象:
JSONP_getPerson({
"name":"怪诞咖啡",
"age":18,
"job":"前端攻城狮",
});
也就是说此文件返回的结果调用了JSONP_getPerson函数,而且把{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}传进去,这{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}是一个对象。此时咱们的页面中有一个JSONP_getPerson函数,函数JSONP_getPerson就被调用到,而且传入了一个对象。此时就实现了在本域获取其余域数据的功能,也就是跨域。
JSONP_getPerson:前端引入远程js并定义好JSONP_getPerson函数,注意须要先定义好JSONP_getPerson函数,避免在远程js加载完成并调用JSONP_getPerson时,此函数不存在
例子代码:
<script>
function JSONP_getPerson(users) {
console.dir(users);
}
</script>
<script src="http://xxx/index.php"></script>
// 前端代码调用script标签块必须在函数标签块以后
地址说明:http://xxx/index.php,其中的xxx是表示不一样域名下的文件,测试的时候,把前端和后端代码放到不一样的源中测试
<?php
echo 'JSONP_getPerson({
"name":"怪诞咖啡",
"age":18,
"job":"前端攻城狮",
})';//返回一个js函数的调用
?>
为何script标签引入的文件不受同源策略的限制?
JSONP的缺点则是:
地址说明:http://xxx/getPerson.php?name=Hello&age=18,其中的xxx是表示不一样域名下的文件,测试的时候,把前端和后端代码放到不一样的源中测试
感悟:
2、动态建立script标签(单项跨域-通常用于获取数据)
这种方法实际上是JSONP跨域的简化版,JSONP只是在此基础上加入了回调函数。好比上例中的 getPerson.php 返回的若是不是一个js函数的调用,而是一个js变量,如:
<?php echo 'var person = {"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}' ?>
那么在当前域下就能够取到person变量,这里须要注意判断script节点是否加载完毕,如:
<script>
var head= document.getElementsByTagName('head')[0];
var scriptPerson= document.createElement('script');
scriptPerson.type= 'text/javascript';
scriptPerson.src= 'http://xxx/index.php';
head.appendChild(scriptPerson);
scriptPerson.onload = scriptPerson.onreadystatechange = function() {
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
console.log(person); //此处取出其余域的数据
scriptPerson.onload = scriptPerson.onreadystatechange = null;
}
};
</script>
地址说明:http://xxx/index.php,其中的xxx是表示不一样域名下的文件,测试的时候,把前端和后端代码放到不一样的源中测试
3、window.name属性(单项跨域-通常用于获取数据)
来源:
特征:
基本原理:
例子:(index.html通过3秒跳转到data.html)
<script>
// 页面index.html
window.name ='我是index.html页面的window.name值';
setTimeout(function(){
window.location = 'http://xxx/data.html';
},3000);
</script>
// 页面data.html,二者不一样域名
<script>
console.log(window.name);
</script>
代码说明:实际运行中可以看到在 data.html 页面上成功获取到了,它的上一个页面 index.html 给window.name设置的值。若是在以后全部载入的页面都没对 window.name 进行修改的话,那么全部这些页面获取到的 window.name 的值都是 index.html 页面设置的那个值。固然,若是有须要,其中的任何一个页面均可以对window.name的值进行修改。注意, window.name 的值只能是字符串的形式,这个字符串的大小最大能容许2M左右,具体状况取决于不一样的浏览器,但通常是够用了。
上面的例子中,咱们用到的页面 index.html 和 data.html 在同域和不一样域环境下都进行了测试,结果都同样,这也正是利用window.name进行跨域的原理。
问题:这样获取到的 window.name 的值须要跳转页面获取,天然不是咱们想要的结果,咱们想要的是页面不跳转也能够获取到数据,上面的例子是为了体现和理解 window.name 的跨域能力,这种简单的方法才更有利于初学者理解和学习。
实现不跳转请求数据:接下来咱们运用 iframe+window.name 来实现,页面不跳转来获取数据,方法就是:在 index.html 页面中使用一个隐藏的 iframe 来充当一个中间人的角色,由 iframe 去获取 data.index 的数据,而后 index.html 再去获得 iframe 获取到的数据。
例子 => 三个文件:
index.html和empty.html必须在同一域,data.html为其余域的数据文件
<script>
// index.html
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name; // 读取数据
console.log(data); // 打印出'{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}',是字符串类型
} else if (state === 0) {
state = 1;
iframe.contentWindow.location = "empty.html"; // 设置的代理文件
}
};
iframe.src = 'http://xxx/data.html';
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
</script>
<script>
// data.html
window.name = '{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}';
</script>
地址说明:http://xxx/data.html,其中的xxx是表示不一样域名下的文件,测试的时候,把前端和后端代码放到不一样的源中测试
我在最初探索window.name+iframe的时候遇到的坑:把 index.html 和 empty.html 没有体如今同一域中(好比页面地址使用 http://localhost/index.html ,里面iframe 的跳转地址使用 iframe.contentWindow.location = 'http://127.0.0.1/empty.html')是不能够的,检测为不是同一个域,缘由请看下面介绍
localhost与127.0.0.1的区别是什么?
localhost也叫local ,正确的解释是:本地服务器
127.0.0.1在windows等系统的正确解释是:本机地址(本机服务器)
他们的解析经过本机的host文件,windows自动将localhost解析为127.0.0.1
双向跨域:两个iframe之间或者两个页面之间,通常用于获取对方数据,document.domain方式还能够直接操做对方DOM
4、document.domain(两个iframe之间或者相同一级域名不一样的二级域名cookie传递,属于双向跨域)
cookie:
Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。可是,两个网页一级域名相同,只是二级域名不一样,浏览器容许经过设置document.domain共享 Cookie。
举例来讲,A网页是 http://w1.example.com/a.html ,B网页是 http://w2.example.com/b.html ,那么只要设置相同的 document.domain ,两个网页就能够共享Cookie。
两个域名必须属于同一个基础域名!并且所用的协议,端口都要一致,不然没法利用document.domain进行跨域
iframe:
和cookie同理,都是须要设置,document.domain
<script>
document.domain = 'example.com';
</script>
问题:一、安全性,当一个站点被攻击后,另外一个站点会引发安全漏洞。二、若是一个页面中引入多个iframe,要想可以操做全部iframe,必须都得设置相同domain。
5、window.postMessage(两个iframe之间或者两个页面之间,属于双向跨域)
HTML5为了解决这个问题,引入了一个全新的API:跨文档通讯 API(Cross-document messaging)。
这个API为 window 对象新增了一个 window.postMessage 方法,容许跨窗口通讯,不论这两个窗口是否同源。
window.postMessage() 方法能够安全地实现跨源通讯。一般,对于两个不一样页面的脚本,只有当执行它们的页面位于具备相同的协议(一般为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain 设置为相同的值) 时,这两个脚本才能相互通讯。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。
6、location.hash(两个iframe之间,属于双向跨域),又称FIM,Fragment identifier Messaging的简写
概念:片断标识符(fragment identifier)指的是,URL的#号后面的部分,好比 http://example.com/x.html#fragment的#fragment 。若是只是改变片断标识符,页面不会从新刷新。
下面是hash不刷新页面,更新hash的例子:
setTimeout(function(){
location.href= 'index.html' + "#" + '{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}';
},1000);
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
console.log(message.slice(1)); //{"name":"怪诞咖啡","age":18,"job":"前端攻城狮"}
}
hash实现跨域的方式:两个文件,一个 index.html 文件,一个是不一样域名下的 index.php 文件
// index.html
<script>
function getData(url, fn) {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = url;
iframe.onload = function() {
fn(iframe.contentWindow.location.hash.substring(1));
window.location.hash = '';
document.body.removeChild(iframe);
};
document.body.appendChild(iframe);
}
// get data from server
var url = 'http://127.0.0.1/index.php';
getData(url, function(data) {
var jsondata = JSON.parse(data);
console.log(jsondata);
});
</script>
// index.php
<?php
$data = '{\"name\":\"怪诞咖啡\",\"age\":18,\"job\":\"前端攻城狮\"}';
echo
"
<script>
window.location = 'http://localhost/index.html' + '#' + \"$data\";
</script>
"
?>
若是看懂了以前利用 window.name+iframe 跨域获取数据,那么使用 window.hash+iframe 也就很好理解了。同样都是动态插入一个iframe,而后把iframe的src指向服务端地址,而服务端一样都是输出一段js代码,一样都是利用和子窗口之间的通讯完成数据传输,一样要针对同源策略作出处理。
HTML标签和跨域
看到以前的介绍,可以了解到跨域都是经过HTML标签作一些事情,HTML有:script、img、iframe、link
CSS中,有伟大的 background 属性,也能够实现跨域
跨域的方法,能够说不少不少,不只仅局限于上面的方法,从此慢慢来总结这块