跨域:通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据或者通过js获取页面中不同域的框架(iframe)中的数据。只要协议、域名、端口有任何一个不同,都被当作是跨域。
下表给出了相对http://store.company.com/dir/page.html同源检测的结果:
跨域解决方法:
一、jsonp
跨域原理:利用script标签没有跨域限制,实现跨域目的
示例:a.html页面需要获取不同域上的json数据,这个json数据地址是http://example.com/data.php,代码如下
a.html
data.php
最终结果
解析:
通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
jquery做法;
解析:
原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
二、document.domain
背景描述:浏览器都有一个同源策略,其限制之一就是不能用ajax的方法去请求不同源中文档;第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的;
但是不同框架之间(父子或同辈),是能购获取到彼此的window对象的,但是不能获取到window对象的属性和方法,可以说是只能获取到一个几乎无用的window对象。
比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。
这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。
跨域原理:通过将不同子域名的document.domain属性设置为相同的父域名,来实现不同子域名之间的跨域通信。
示例:
在页面 http://www.example.com/a.html 中设置document.domain:
在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:
这样我们就可以通过js访问到iframe中的各种属性和对象了。
注意:如果你想在http://www.example.com/a.html 页面中通过ajax直接请求http://example.com/b.html 页面,即使你设置了相同的document.domain也还是不行的,所以修改document.domain的方法只适用于不同子域的框架间的交互。
三、window.name
跨域原理:window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置,即使是不同域间也是适用的。
注意:window.name的值只能是字符串的形式,最大能允许2M左右或者更大,具体取决于不同的浏览器,但一般是够用了
示例:
1、同一个域下
a.html
b.html
结果
2、不同域下
比如有一个http://www.myapp.com/window_name_test.ejs页面,需要通过js来获取另一个位于不同域上的页面http://www.otherapp.com/window_iframe里的数据。
访问页面:http://www.myapp.com/window_name_test.ejs
<button>发送消息</button>
<div class="req"></div>
<div class="res"></div>
<script>
function sendMsg(msg) {
var state = 0, data;
var frame = document.createElement("frame");
var baseProxy = "http://www.otherapp.com/window_iframe";
var request = {data: msg};
frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request));
frame.style.display = "none";
frame.onload = function() {
if (state === 1) {
data = frame.contentWindow.name;
document.querySelector('.res').innerHTML = "获得响应:" + data;
// 删除iframe
frame.contentWindow.document.write('');
frame.contentWindow.close();
document.body.removeChild(frame);
} else {
state = 1;
frame.src = "http://www.myapp.com/window_iframe";
}
};
document.body.appendChild(frame);
}
document.querySelector('button').onclick = function() {
var val = Math.random();
sendMsg(val);
document.querySelector('.req').innerHTML = "请求数据:" + val;
}
</script>
跨域iframe:http://www.myapp.com/window_iframe
<script>
var hash = window.location.hash;
if (hash && hash.length > 1) {
var request = hash.slice(1, hash.length);
var obj = JSON.parse(decodeURI(request));
var data = obj.data;
window.name = data*100;
}
</script>
解析:
使用一个隐藏的iframe充当一个中间人的角色,先创建一个隐藏的iframe b,将b指向外部资源服务;
b加载完成之后,将相应的数据附加到window.name上;
目前a和b不同源,a还是不能获取到b的window.name;所以在b获取到数据之后,将页面导航到任何一个与a同源的页面,这时a就可以获取到b的name值;当a获取到数据之后,就可以随时删掉b