只要协议、域名、端口有任何一个不同,都被当作是不同的域。对于端口和协议的不同,只能通过后台来解决。
协议:http 主域名:cnblogs.com 子域名:www 端口号: 2050
CORS(跨域资源共享)
原理:使用自定义的HTTP头部让浏览器与服务器进行沟通
并不是所有的浏览器都支持
Node.js中的应用
//allow custom header and CORS
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*'); //设置允许的域
res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');//允许的header类型
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE,OPTIONS');
if (req.method == 'OPTIONS'){
res.send(200); /让options请求快速返回/
}
else {
next();
}
});
JSONP
原理: 网络上javascript的请求不存在跨域限制,动态插入script标签
优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;
在请求完毕后可以通过调用callback的方式回传结果。
缺点:只能实现get请求,不能实现post请求;(单域操作)
它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript
调用的问题。
服务端
//通过require将http库包含到程序中
var http = require('http');
//引入url模块解析url字符串
var url = require('url');
//引入querystring模块处理query字符串
var querystring = require('querystring');
//创建新的HTTP服务器
var server = http.createServer();
//通过request事件来响应request请求
server.on('request',function(req, res){
var urlPath = url.parse(req.url).pathname;
var qs = querystring.parse(req.url.split('?')[1]);
if(urlPath === '/jsonp' && qs.callback){ //先判断是不是jsonp请求,如果是就把请求头设置为json
res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});
var data = {
"name": "Monkey"
};
data = JSON.stringify(data);
var callback = qs.callback+'('+data+');';
res.end(callback);
}
else{
res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'}); //否则请求不变
res.end('Hell World\n');
}
});
//监听8080端口
server.listen('8080');
//用于提示我们服务器启动成功
console.log('Server running!');
前端页面
<!DOCTYPE html>
<head>
<title>jsonp</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<script>
function test(data){
alert(data.name);
};
</script>
<scriptsrc="http://127.0.0.1:8080/json?callback=test"></script> //在请求的地址拼接json和回调函数的名称
</body>
</html>
通过修改document.domain+ifame来跨子域
对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain= ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com那显然是会报错地!代码如下:
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';
这种方式适用于{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何页面相互通信。
备注:某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。 domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。
问题:
1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。
同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。但是不同的框架之间(父子或同辈),是能够获取到彼此的window对象的。
只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。注意:只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,
window.name
www.a.com/c1.html
www.a.com/代理.html
www.b.com/c2.html 下写入数据window.name = ‘数据a’
在c2下创建一个c1的代理文件,与c2共用数据。请求c1.html时可以通过代理。
优点: 既不复杂,也能兼容到几乎所有浏览器,安全
利用iframe和location.hash
<iframe src= “xxx.b.com#key1=value1&key2=value2”></iframe>
更新a页面的hash值:parent.location.hash =
IE和chrome不支持
在a.com中添加一个页面,设置parent.location.hash =;然后再设置parent.location.hash =self.location.hash;
HTML5中新引进的window.postMessage
适用于跨子域
在 HTML5 规范中,除了 XDM 部分之外的其他部分也会提到这个方法名,但都是为了同一个目的:向另一个地方传递数据。对于 XDM 而言,“另一个地方”指的是包含在当前页面中的<iframe>元素,或者由当前页面弹出的窗口。
postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。来看下面的例子。
//注意:所有支持 XDM 的浏览器也支持iframe 的 contentWindow 属性
var iframeWindow =document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("Asecret", "http://www.wrox.com");
最后一行代码尝试向内嵌框架中发送一条消息,并指定框架中的文档必须来源于"http://www.wrox.com"域。如果来源匹配,消息会传递到内嵌框架中;否则, postMessage()什么也不做。这一限制可以避免窗口中的位置在你不知情的情况下发生改变。如果传给 postMessage()的第二个参数是"*",则表示可以把消息发送给来自任何域的文档,但我们不推荐这样做。
接收到 XDM 消息时,会触发 window 对象的 message 事件。这个事件是以异步形式触发的,因此从发送消息到接收消息(触发接收窗口的 message 事件)可能要经过一段时间的延迟。触发 message事件后,传递给 onmessage 处理程序的事件对象包含以下三方面的重要信息。
q data:作为 postMessage()第一个参数传入的字符串数据。
q origin:发送消息的文档所在的域,例如"http://www.wrox.com"。
q source:发送消息的文档的 window 对象的代理。这个代理对象主要用于在发送上一条消息的
窗口中调用 postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。
接收到消息后验证发送窗口的来源是至关重要的。就像给 postMessage()方法指定第二个参数,
以确保浏览器不会把消息发送给未知页面一样,在 onmessage 处理程序中检测消息来源可以确保传入
的消息来自已知的页面。基本的检测模式如下。
EventUtil.addHandler(window,"message", function(event){
//确保发送消息的域是已知的域
if(event.origin == "http://www.wrox.com"){
//处理接收到的数据
processMessage(event.data);
//可选:向来源窗口发送回执
event.source.postMessage("Received!","http://p2p.wrox.com");
}
});
用jQuery的ajax解决跨域问题的简单版:
jQuery.ajax()支持get方式的跨域,这其实是采用jsonp的方式来完成的。
$.ajax({
async:false,
url:'http://www.mysite.com/demo.do', // 跨域URL
type:'GET',
dataType:'jsonp',
jsonp:'jsoncallback', //默认callback
data:mydata,
timeout:5000,
beforeSend:function(){ //jsonp 方式此方法不被触发。原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
},
success:function (json) { //客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数
if(json.actionErrors.length!=0){
alert(json.actionErrors);
}
genDynamicContent(qsData,type,json);
},
complete:function(XMLHttpRequest, textStatus){
$.unblockUI({fadeOut: 10 });
},
error:function(xhr){
//jsonp方式此方法不被触发
//请求出错处理
alert("请求出错(请检查相关度网络状况.)");
}
});
function(json){
if(json.属性名==值){
//执行代码
}
});
这种方式其实是上例$.ajax({..}) api的一种高级封装,有些$.ajax api底层的参数就被封装而不可见了。