跨域是当前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' }
});

 

posted on 2014-07-20 23:54  zhusheng  阅读(240)  评论(0编辑  收藏  举报