JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。因为同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。
下表给出了相对 http://site.baidu.com/file/index.html 同源检测的示例:
序号 | URL | 结果 | 原因 |
---|---|---|---|
1 | http://site.baidu.com/file/inner/1.html | 成功 | 同一域名 |
2 | http://site.baidu.com/file2/2.html | 成功 | 同一域名不同文件夹 |
3 | https://site.baidu.com/secure.html | 失败 | 不同协议 ( https ) |
4 | http://site.baidu.com:81/file/etc.html | 失败 | 不同端口 ( 81 ) |
5 | http://wiki.baidu.com/file/other.html | 失败 | 不同主机 ( wike) |
1、主域相同的跨域
主域相同的跨域
document.domain的场景只适用于不同子域的框架间的交互,及主域必须相同的不同源。
页面可能会更改其自己的来源,但有一些限制。脚本可以将 document.domain的值设置为其当前域或其当前域的超级域。如果将其设置为其当前域的超级域,则较短的域将用于后续原始检查。例如,假设文档中的一个脚本在 http://site.baidu.com/file/2.html 执行以下语句:
document.domain = "baidu.com";
这条语句执行之后,页面将会成功地通过对 http://baidu.com/file/index.html 的同源检测。
//www.a.com上的a.html document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
}; //script.a.com上的b.html document.domain = 'a.com';
注:
1. 某一页面的domain默认等于window.location.hostname。
2. 主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。
3. domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。
4. 使用document.domain允许子域安全访问其父域时,需要设置document域在父域和子域中具有相同的值。这是必要的,即使这样做只是将父域设置回其原始值。否则可能会导致权限错误。这里都是a.com。
2. 动态创建script
虽然浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以*执行引入的JS文件中的function(包括操作cookie、Dom等等)。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信。具体的做法可以参考YUI的Get Utility
这里判断script节点加载完毕还是蛮有意思的:ie只能通过script的readystatechange属性,其它浏览器是script的load事件。以下是部分判断script加载完毕的方法。
js.onload = js.onreadystatechange = function() {
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
// callback在此处执行
js.onload = js.onreadystatechange = null;
}
};
优点:简单适用,老式浏览器全部支持,服务器改造小。不需要XMLHttpRequest或ActiveX的支持。
缺点:只支持GET请求。
3. 使用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的实时消息传递。
各浏览器兼容性如下:
一个简单的例子:
var popup = window.open('http://bbb.com', 'title');//父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法。
popup.postMessage('Hello World!', 'http://bbb.com');
postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。
父窗口和子窗口都可以通过message事件,监听对方的消息。message事件的事件对象event,提供以下三个属性:
- 1、event.source:发送消息的窗口。
- 2、event.origin: 消息发向的网址。
- 3、event.data:消息内容。
var onmessage = function (event) {
var data = event.data;//消息
var origin = event.origin;//消息来源地址
var source = event.source;//源Window对象
if(origin == "http://www.aaa.com"){
console.log(data);//hello world!
}
source.postMessage('Nice to see you!', '*');
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//ie
window.attachEvent('onmessage', onmessage);
}
4. 通过CORS跨域
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。所有浏览器都支持该功能,IE浏览器不能低于IE10
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
具体信息可以看下这篇博客:http://www.ruanyifeng.com/blog/2016/04/cors.html