您好,游客注册登录繁體
首页 >> javascript >> Javascript跨域问题详谈

Javascript跨域问题详谈

Gavin 2015-03-17 0
浏览次数1753

跨域是什么?

假设a.com/get.html需要获取b.com/data.html中的数据,而这里a.com和b.com并不是同一台服务器,这就是跨域跨域会涉及到Javascript的同源策略,简单来说就是为了保护网站的安全,不被外域(非同源)服务器的js修改本网站内容。

引用一个表格,看一下引起跨因的条件有哪些:

表格

但是有时候我们确实需要这么做,那么我们有哪些方法呢?

1、JsonP

提到跨域不能不先提及jsonp。jsonp其实是JavacScript Object Notation with Padding的简称,可以理解成填充了内容的json格式数据。

因为

以上声明了callback并且调用外域b.com的data.js,而data.js中调用:

callback({msg:"tqtan"});

这样当调用引用外域的js就会调用本地的callback()从而实现数据传输。

上面是只是简单的跨域,我们来看jQuery的真正的运用。

jQuery中的ajax可拉取外域的数据,通过两种方法:

1、$.getJSON()

这种方法简单粗暴,请求外域Json。

$.getJSON("http://b.com/dataServlet?callback=?",function(data){

console.log(data.msg);

});

假设上述请求访问b.com下的servlet页面,传的参数为callback=?,jQuery会自动生成字符串填补占位符?,例如callback=jQuery17207481773362960666_1332575486681。这就声明了与服务器的唯一标示,服务器只需要返回带有这个callback值的json格式数据即可,例如:

//dataServlet.java

String callback = req.getParameter("callback");

PrintWriter out = resp.getWriter();

out.print(callback+"('msg','tqtan')");

这样就能成功获取非同源服务器的数据了。

2、$.ajax()

实现原理和上面一样,只是可以自定义更多链接。

$.ajax({

url:'http://b.com/dataServlet?words=hi',

dataType:'jsonp',

jsonp : 'jsoncallback',

jsoncallback : 'tqtan',

success:function(data){

console.log(data.msg);

},

error: function (e) {

console.log(e);

}

});

可以自定义callback的名字,这里改为’tqtan’,同时这里可以传值words=hi。
注意了,JsonP格式只能是以GET形式请求服务器。

2、document.domain

这种方法只适用于主域相同,而子域不同的跨域。

也就是get.a.com和data.a.com的跨域问题,解决方法很简单:

若get.a.com/get.html需要获取data.a.com/data.html的数据,首先在get.html插入一个iframe,src指向data.a.com/data.html,然后在data.html写上document.domain=’a.com’;即可操纵data.html内的内容。

//get.html

var iframe = document.creatElement("iframe");

iframe.src="http://data.a.com/data.html";

iframe.style.display="none";

document.body.appendChild(iframe);

document.domain = 'a.com';

iframe.onload = function(){

var otherDocument = iframe.contentDocument || iframe.contentWindow.document;

//otherDocument就是另一个页面的document

//do whatever you want..

};

//data.html

document.domain = 'a.com';

3、url hash

你也可以通过url的hash来实现跨域。hash就是url#后面的内容,例如http://targetkiller.net/index.html#data,这里#data就是hash。怎么用这个实现跨域呢?

还是那个例子,a.com/get.html需要获取b.com/data.html,首先在get.html建立一个iframe,src还是指向data.html,后面带上hash值实现传参。另一端data.html根据获取的hash作出响应,自身也创建一个iframe,src指向a.com/proxy.html,并把响应数据添加到hash。之后,a.com/proxy.html只需要修改在同一a.com父域的get.html的hash即可。最后,怎样获取数据呢?只需要在get.html写一个定时器setInterval,定期监听有无新的hash即可。

看到这里,你可能感到开始乱了,几个问题:

1.proxy.html的作用?

由于get.html和data.html不在一个域上,所以不能修改location.hash值,于是运用proxy.html,先跳到找个代理页面,然后通过parent.location.hash,也就是修改父亲,让儿子(get.html)也得到响应。

a.com/get.html

var iframe = document.createElement('iframe');

iframe.src = 'http://a.com/get.html#data';

iframe.style.display = 'none';

document.body.appendChild(iframe);

//周期检测hash更新

function getHash() {

var data = location.hash ? location.hash.substring(1) : '';

console.log(data);

}

var hashInt = setInterval(function(){getHash()}, 1000);

a.com/proxy.html

parent.location.hash = self.location.hash.substring(1);

b.com/data.html

//模拟一个简单的参数处理操作

if(location.hash){

var data = location.hash;

doSth(data);

}

function doSth(data){

console.log("from a.com:"+data);

var msg = "hello i am b.com";

var iframe = document.createElement('iframe');

iframe.src = "http://a.com/proxy.html#"+msg;

iframe.style.display = 'none';

document.body.appendChild(iframe);

}

4、window.name

这种方法比较巧妙,引用圆心的解释,name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

具体例子依旧如上,同时也是需要一个代理页面。

a.com/get.html请求b.com/data.html,首先get.html创建一个iframe,src指向data.html,然后监听iframe的onload事件。与此同时,在data.html设置window.name = data;把window.name赋值。然后onload事件后马上把iframe的跳到本地a.com/proxy.html。因此window.name就共享到了src为proxy.html的找个iframe中,接下来,就是同源间获取值的事了。

a.com/get.html

var state = 0,

iframe = document.createElement('iframe'),

iframe.src = 'http://b.com/data.html";

iframe.style.display = 'none';

loadfn = function() {

if (state === 1) {

var data = iframe.contentWindow.name;

console.log(data);

} else if (state === 0) {

state = 1;

//跳到proxy.html

iframe.contentWindow.location = "http://a.com/proxy.html";

}

};

if (iframe.attachEvent) {

iframe.attachEvent('onload', loadfn);

} else {

iframe.onload = loadfn;

}

document.body.appendChild(iframe);

a.com/proxy.html

// proxy.html的操作主要是删除get.html的iframe,避免安全问题发生

iframe.contentWindow.document.write('');

iframe.contentWindow.close();

document.body.removeChild(iframe);

b.com/data.html

var data = "hello,tqtan";

window.name = data;

5、 postMessage()

html5的新方法postMessage()优雅地解决了跨域,也十分容易理解。

发送方调用postMessage()内容,接受方监听onmessage接受内容即可。

假设发送方为a.com/send.html,接受方为b.com/receive.html。

a.com/send.html

var iframe = document.createElement("iframe");

iframe.src = "http://b.com/receive.html";

document.body.appendChild(iframe);

iframe.contentWindow.postMessage("hello","http://b.com");

b.com/receive.html

window.addEventListener('message', function(event){

// 通过origin属性判断消息来源地址

if (event.origin == 'http://a.com') {

console.log(event.data);

console.log(event.source);//发送源的window值

}

}, false);

6、CORS(后台实现)

以上5点都是前端实现的跨域,但是后台参与会让跨域更容易解决,也就是用CORS。

CORS是Cross-Origin Resource Sharing的简称,也就是跨域资源共享。它有多牛逼?之前说JsonP只能get请求,但CORS则可以接受所有类型的http请求,然而CORS只有现代浏览器才支持。

怎样使用?前端只需要发普通ajax请求,注意检测CORS的支持度。引用自蒋宇捷。

function createCORSRequest(method, url) {

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr) {

// 此时即支持CORS的情况

// 检查XMLHttpRequest对象是否有“withCredentials”属性

// “withCredentials”仅存在于XMLHTTPRequest2对象里

xhr.open(method, url, true);

}

else if (typeof!= "undefined") {

// 否则检查是否支持XDomainRequest,IE8和IE9支持

// XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式

xhr = new XDomainRequest();

xhr.open(method, url);

} else {

// 否则,浏览器不支持CORS

xhr = null;

}

return xhr;

}

var xhr = createCORSRequest('GET', url);

if (!xhr) {

throw new Error('CORS not supported');

}

与此同时,服务器端只需要设置Access-Control-Allow-Origin头即可。

java中你只需要设置

response.setHeader("Access-Control-Allow-Origin", "*");

为了安全,也可以将*改为特定域名,例如a.com。

学习来源:http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html

http://blog.csdn.net/hfahe/article/details/7730944

http://targetkiller.net/cross-domain/

全部评论:0

需要登录才能发表评论!

需要 登录 才能发表评论

<