跨域是当前Web开发不可忽视的情况,经常会遇到跟其他站点进行通信情况,跨域主要包括以下情况:
1. 同域名,不同端口
2. 同主域名,不同子域名
3. 同域名,不同协议
4. 不同域名
解决方案:
对于第二种情况可以使用,可以使用设置document.domain = xx.com来解决:
例如first.domain.com,second.domain.com 或者domain.com 这种情况下属于主域名相同
第一步在所有页面中设定 document.domain = "domain.com";
第二步创建Iframe然后通过
<script type=”text/javascript” >
document.domain = 'domain.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://second.domain.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);
};
//first.domain.com通过下面代码获取second.domain.com窗口的高度
var h = window.navigator.userAgent.indexOf("MSIE") > 0 ? document.body.scrollHeight : document.documentElement.scrollHeight;
parent.parent.document.getElementById('name').height = h;
//两个不同的子域名伪装成了同域,完全在同源策略的保护下进行通信
</script>
其他情况可以使用以下方式:
1. 服务端代理:
客户端在请求本域名的服务端脚本(PHP,.NET,Jsp或其他CGI), 服务端通过http请求目标域名,由于服务端是可以跨域的,所以服务端作为代理请求目标域名数据返回给本地域名,
好处是简单有效,坏处是经过服务端介入,性能稍微有点损耗。
2. iframe+hash
不同域名下可以通过iframe嵌入,然后通过hash传递数据,缺点是hash长度有限,并且不够安全,只能传递字符串
例如www.domain.com#abc 其中abc就是location.hash
例如a.com想访问b.com
第一步a.com创建指向b.com#abc的iframe
第二步a.com通过定时器检测iframe中的hash是否变化,如果变化重新获取hash值,IE,chrome浏览器不支持跨域修改parent.location.hash值,所以需要代理,但是firefox支持
a.com代码
function startRequest(){
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://www.b.com/b.html#paramdo'; //根据paramdo来决定获取啥值
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
if (console.log)
{
console.log('Now the data is '+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000);
b.com页面的代码如下
//模拟一个简单的参数处理操作
switch(location.hash){
case '#paramdo':
callBack();
break;
case '#paramset':
//do something……
break;
}
//设置a.com iframe src的location.hash
function callBack(){
try {
//此时是火狐正常执行
parent.location.hash = 'somedata';
} catch (e) {
// ie、chrome的安全机制无法修改parent.location.hash,
// 所以要利用一个中间的cnblogs域下的代理iframe
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://a.com/change.html#somedata'; // 注意该文件在"a.com"域下
document.body.appendChild(ifrproxy);
}
}
//a.com域名下 change.html中的代码
//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
3. Jsonp或动态创建script
Jsonp的原理就是利用<script>标签可以引入其他域名资源的方法,在需要跨域的脚本返回Json格式字符串,同时带上方法名,将Json作为参数返回,在本地服务器定义了该方法用来解析Json字符串,从而达到将跨域数据下载到本地的方式,例如:
服务器 www.local.com 文件local.html想访问 www.remote.com上的文件remote.php
local.html代码:
<html>
<script src="http://www.remote.com/remote.php?call_back=ShowJson"></script>
<script>
funtcion ShowJson(json)
{
//解析json
}
</script>
</html>
remote.php
<?php
$call_back = $_GET['call_back'];
$data = array("name"=>"michael","sex"=>"male");
echo $call_back.'('.json_encode($data).')';
?>
这样本地文件可以跨域访问其他域名的数据,条件是其他域名文件需要进行call_back处理
4. window.name
window.name属性是同一个窗口生命周期内的全局变量,就是跳转的各个页面都可以访问同一个window.name对象,在跨域的场景中,可以通过代理iframe的方式同样获得window.name值例子:
a.com下的a.html
<script type="text/javascript">
function domainData(url, fn)
{
var isFirst = true;
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
var loadfn = function(){
if(isFirst){
iframe.contentWindow.location = 'http://a.com/null.html';
isFirst = false;
} else {
fn(iframe.contentWindow.name);
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
iframe.src = '';
iframe = null;
}
};
iframe.src = url;
if(iframe.attachEvent){
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
}
domainData('http://b.com/data.html', function(data){
alert(data);
});
</script>
b.com下的data.html
<script> window.name = '需要跨域传递的数据'; </script>
null.html 是必须的。内容可为空。
调用domainData函数必须在body后面,或页面加载完后。
调用时会执行 http://b.com/data.html 页面的脚本。
5. HTML5 postMessage
Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+中可以使用HTML5新特性postMessage 来进行跨域处理:
a.html
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
var ifr = document.getElementById('ifr');
var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果一样
// 若写成'http://c.com'就不会执行postMessage了
ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
b.html
<script type="text/javascript">
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://a.com') {
alert(event.data); // 弹出"I was there!"
alert(event.source); // 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
}
}, false);
</script>
6. 使用JS框架,如jQuery和ExtJs
jQuery和ExtJs已经封装了跨域Ajax请求,基本原理也是利用Jsonp方式,使用方法如下:
1) jQuery:
简化方式
//跨域(可跨所有域名)
$.getJSON("http://user.hnce.com.cn/getregion.aspx?id=0&jsoncallback=?",function(json){
//要求远程请求页面的数据格式为: ?(json_data)
//例如:
//?([{"_name":"湖南省","_regionId":134},{"_name":"北京市","_regionId":143}])
alert(json[0]._name);
});
定制化方式:
$.ajax({
type : "get",
async:false,//是否异步
url : "http://www.xxx.com/ajax.do",
dataType : "jsonp",
jsonp: "callbackparam",//服务端用于接收callback调用的function名的参数
jsonpCallback:"success_jsonpCallback",//callback的function名称
success : function(json){
alert(json);
alert(json[0].name);
},
error:function(){
alert('fail');
}
});
//服务端代码:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
String callbackFunName = context.Request["callbackparam"];
context.Response.Write(callbackFunName + "([ { name:\"John\"}])");
}
2)Extjs跨域方式:
ExtJS4.1的Ext.data.JsonP.request实现跨域访问:
//跨域请求,MsgUrl为其他站点地址
Ext.data.JsonP.request({
url: MsgUrl + '/Home/InitializeComet',
timeout: 300000,
params: { loginId: LoginId },
callbackKey: "jsonPCallback",
success: function(result) {
if (result.rettype == 'true') {
me.Comet.privateToken = result.msg;
me.RegisterComet();
} else {
alert(result.msg);
}
},
failure: function(result) {
alert(result);
}
});
使用ExtJs4.0以前的Ext.data.ScriptTagProxy方式:
var ss = new Ext.data.ScriptTagProxy({
//url: 'http://10.128.3.104/edi/rest/GetBillCaseInfo',
url: 'testjson.do',
callbackParam: "_callback",
headers: { 'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=' }
});
ss.load({ '_out': 'json' },
new Ext.data.JsonReader(
{ root: "ROWSET.ROW" },
[{ name: 'CaseCode', mapping: 'CaseCode' },{ name: 'CaseName', mapping: 'CaseName'}]),
function (recordsBlock, arg, isok) {
alert(Ext.encode(recordsBlock));
alert(Ext.encode(recordsBlock.records[0].data));
}
);
Ext.Ajax.request({
url: 'http://10.128.3.104/edi/rest/GetBillCaseInfo',
//url: 'testjson.do',
scriptTag: true,
success: function (req) {
alert(req.responseText);
},
failure: function (req) {
alert(req.responseText);
},
headers: { 'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=' },
params: { _out: 'json' }
});
浙公网安备 33010602011771号