首頁  >  文章  >  web前端  >  深入分析Javascript跨域問題_javascript技巧

深入分析Javascript跨域問題_javascript技巧

WBOY
WBOY原創
2016-05-16 16:03:261112瀏覽

跨域是什麼?

假設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。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn