vue跨域解决方案websocket_前端跨域解决方案汇总
編者注:關(guān)于跨域的文章,之前分享過很多,來看看這篇前端跨域解決方案,由簡(jiǎn)及深介紹各種存在的跨域請(qǐng)求解決方案,包括 document.domain, location.hash, window.name, window.postMessage, JSONP, WebSocket, CORS。
原文:hijiangtao.github.io
同源策略限制從一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。但是有時(shí)候跨域請(qǐng)求資源是合理的需求,本文嘗試從多篇文章中匯總至今存在的所有跨域請(qǐng)求解決方案。
跨域請(qǐng)求
首先需要了解的是同源和跨源的概念。對(duì)于相同源,其定義為:如果協(xié)議、端口(如果指定了一個(gè))和主機(jī)對(duì)于兩個(gè)頁(yè)面是相同的,則兩個(gè)頁(yè)面具有相同的源。只要三者之一任意一點(diǎn)有不同,那么就為不同源。當(dāng)一個(gè)資源從與該資源本身所在的服務(wù)器的域或端口不同的域或不同的端口請(qǐng)求一個(gè)資源時(shí),資源會(huì)發(fā)起一個(gè)跨域 HTTP 請(qǐng)求。而有關(guān)跨域請(qǐng)求受到限制的原因可以參考如下 MDN 文檔片段:
跨域不一定是瀏覽器限制了發(fā)起跨站請(qǐng)求,而也可能是跨站請(qǐng)求可以正常發(fā)起,但是返回結(jié)果被瀏覽器攔截了。最好的例子是 CSRF 跨站攻擊原理,請(qǐng)求是發(fā)送到了后端服務(wù)器無論是否跨域!注意:有些瀏覽器不允許從 HTTPS 的域跨域訪問 HTTP,比如 Chrome 和 Firefox,這些瀏覽器在請(qǐng)求還未發(fā)出的時(shí)候就會(huì)攔截請(qǐng)求,這是一個(gè)特例。
解決方法匯總
以下我們由簡(jiǎn)及深介紹各種存在的跨域請(qǐng)求解決方案,包括 document.domain, location.hash, window.name, window.postMessage, JSONP, WebSocket, CORS。
document.domain
document.domain 的作用是用來獲取/設(shè)置當(dāng)前文檔的原始域部分,例如:
// 對(duì)于文檔 www.example.xxx/good.html
document.domain="www.example.xxx"
// 對(duì)于URI http://developer.mozilla.org/en/docs/DOM
document.domain="developer.mozilla.org"
如果當(dāng)前文檔的域無法識(shí)別,那么 domain 屬性會(huì)返回 null。
在根域范圍內(nèi),Mozilla允許你把domain屬性的值設(shè)置為它的上一級(jí)域。例如,在 developer.mozilla.org 域內(nèi),可以把domain設(shè)置為 "mozilla.org" 但不能設(shè)置為 "mozilla.com" 或者"org"。
因此,若兩個(gè)源所用協(xié)議、端口一致,主域相同而二級(jí)域名不同的話,可以借鑒該方法解決跨域請(qǐng)求。
比如若我們?cè)?http://a.github.io 頁(yè)面執(zhí)行以下語(yǔ)句:
document.domain = "github.io"
那么之后頁(yè)面對(duì) github.io 發(fā)起請(qǐng)求時(shí)頁(yè)面則會(huì)成功通過對(duì) github.io 的同源檢測(cè)。比較直接的一個(gè)操作是,當(dāng)我們?cè)赼.github.io 頁(yè)面中利用 iframe 去加載 github.io 時(shí),通過如上的賦值后,我們可以在 a.github.io 頁(yè)面中去操作 iframe 里的內(nèi)容。
我們同時(shí)考慮另一種情況:存在兩個(gè)子域名 a.github.io 以及 b.github.io, 其中前者域名下網(wǎng)頁(yè) a.html 通過 iframe 引入了后者域名下的 b.html,此時(shí)在 a.html 中是無法直接操作 b.html 的內(nèi)容的。
同樣利用 document.domain,我們?cè)趦蓚€(gè)頁(yè)面中均加入
document.domain='github.io'
這樣在以上的 a.html 中就可以操作通過 iframe 引入的 b.html 了。
document.domain 的優(yōu)點(diǎn)在于解決了主語(yǔ)相同的跨域請(qǐng)求,但是其缺點(diǎn)也是很明顯的:比如一個(gè)站點(diǎn)受到攻擊后,另一個(gè)站點(diǎn)會(huì)因此引起安全漏洞;若一個(gè)頁(yè)面中引入多個(gè) iframe,想要操作所有的 iframe 則需要設(shè)置相同的 domain。
location.hash
location.hash 是一個(gè)可讀可寫的字符串,該字符串是 URL 的錨部分(從 # 號(hào)開始的部分)。例如:
// 對(duì)于頁(yè)面 http://example.com:1234/test.htm#part2
location.hash = "#part2"
同時(shí),由于我們知道改變 hash 并不會(huì)導(dǎo)致頁(yè)面刷新,所以可以利用 hash 在不同源間傳遞數(shù)據(jù)。
假設(shè) github.io 域名下 a.html 和 shaonian.eu 域名下 b.html 存在跨域請(qǐng)求,那么利用 location.hash 的一個(gè)解決方案如下:
a.html 頁(yè)面中創(chuàng)建一個(gè)隱藏的 iframe, src 指向 b.html,其中 src 中可以通過 hash 傳入?yún)?shù)給 b.html
b.html 頁(yè)面在處理完傳入的 hash 后通過修改 a.html 的 hash 值達(dá)到將數(shù)據(jù)傳送給 a.html 的目的
a.html 頁(yè)面添加一個(gè)定時(shí)器,每隔一定時(shí)間判斷自身的 location.hash 是否變化,以此響應(yīng)處理
以上步驟中需要注意第二點(diǎn):如何在 iframe 頁(yè)面中修改 父親頁(yè)面的 hash 值。由于在 IE 和 Chrome 下,兩個(gè)不同域的頁(yè)面是不允許 parent.location.hash 這樣賦值的,所以對(duì)于這種情況,我們需要在父親頁(yè)面域名下添加另一個(gè)頁(yè)面來實(shí)現(xiàn)跨域請(qǐng)求,具體如下:
假設(shè) a.html 中 iframe 引入了 b.html, 數(shù)據(jù)需要在這兩個(gè)頁(yè)面之間傳遞,且 c.html 是一個(gè)與 a.html 同源的頁(yè)面
a.html 通過 iframe 將數(shù)據(jù)通過 hash 傳給 b.html
b.html 通過 iframe 將數(shù)據(jù)通過 hash 傳給 c.html
c.html 通過 parent.parent.location.hash 設(shè)置 a.html 的 hash 達(dá)到傳遞數(shù)據(jù)的目的
location.bash 方法的優(yōu)點(diǎn)在于可以解決域名完全不同的跨域請(qǐng)求,并且可以實(shí)現(xiàn)雙向通訊;而缺點(diǎn)則包括以下幾點(diǎn):
利用這種方法傳遞的數(shù)據(jù)量受到 url 大小的限制,傳遞數(shù)據(jù)類型有限
由于數(shù)據(jù)直接暴露在 url 中則存在安全問題
若瀏覽器不支持 onhashchange 事件,則需要通過輪訓(xùn)來獲知 url 的變化
有些瀏覽器會(huì)在 hash 變化時(shí)產(chǎn)生歷史記錄,因此可能影響用戶體驗(yàn)
window.name
該屬性用于獲取/設(shè)置窗口的名稱。其特征在于:一個(gè)窗口的生命周期內(nèi),窗口載入的所有頁(yè)面共享該值,且都具有對(duì)該屬性的讀寫權(quán)限。這意味著如果不修改該值,那么在不同頁(yè)面加載之后該值也不會(huì)變,且其支持長(zhǎng)達(dá) 2MB 的存儲(chǔ)量。
利用該特性我們可以將跨域請(qǐng)求用如下步驟解決:
在 a.github.io/a.html 中創(chuàng)建 iframe 指向 b.github.io/b.html (頁(yè)面會(huì)將自身的 window.name 附在 iframe 上)
給 a.github.io/a.html 添加監(jiān)聽 iframe 的 onload 事件,在該事件中將 iframe 的 src 設(shè)置為本地域的代理文件(代理文件和a.html處于同一域下,可以相互通信),同時(shí)可以傳出 iframe 的 name 值
獲取數(shù)據(jù)后銷毀 iframe,釋放內(nèi)存,同時(shí)也保證了安全
window.name 的優(yōu)勢(shì)在于巧妙地繞過了瀏覽器的跨域訪問限制,但同時(shí)它又是安全操作。
window.postMessage
HTML5 為了解決這個(gè)問題,引入了一個(gè)全新的 API:跨文檔通信 API(Cross-document messaging)。這個(gè) API 為 window 對(duì)象新增了一個(gè) window.postMessage 方法,允許跨窗口通信,不論這兩個(gè)窗口是否同源。
API 的詳細(xì)使用方法請(qǐng)見 MDN。
JSONP
JSONP, 全稱 JSON with Padding,是使用 AJAX 實(shí)現(xiàn)的請(qǐng)求不同源的跨域。其基本原理:網(wǎng)頁(yè)通過添加一個(gè)
以下為一個(gè)例子,由于 test.js 返回的內(nèi)容直接作為代碼運(yùn)行,所以只要 a.html 中定義了 callback 函數(shù), 它就會(huì)立即被調(diào)用。
// 當(dāng)前頁(yè)面 a.com/a.html
//回調(diào)函數(shù)
function callback(data) {
alert(data.message);
}
// test.js
// 調(diào)用callback函數(shù),并以json數(shù)據(jù)形式作為闡述傳遞,完成回調(diào)
callback({message:"success"});
為了保證 script 的靈活,我們可以通過 JavaScript 動(dòng)態(tài)創(chuàng)建 script 標(biāo)簽,并通過 HTTP 參數(shù)向服務(wù)器傳入回調(diào)函數(shù)名,案例如下所示:
// 添加
function addScriptTag(src){
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function(){
// 搜索apple,將自定義的回調(diào)函數(shù)名result傳入callback參數(shù)中
addScriptTag("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=apple&callback=result");
}
// 自定義的回調(diào)函數(shù)result
function result(data) {
// 我們就簡(jiǎn)單的獲取apple搜索結(jié)果的第一條記錄中url數(shù)據(jù)
alert(data.responseData.results[0].unescapedUrl);
}
jQuery 有相應(yīng)的 JSONP 的實(shí)現(xiàn)方法,見 API。
JSONP的優(yōu)點(diǎn)在于簡(jiǎn)單適用,老式瀏覽器全部支持,服務(wù)器改造小。不需要XMLHttpRequest或ActiveX的支持;但缺點(diǎn)是只支持 GET 請(qǐng)求。
WebSocket
WebSocket 協(xié)議不實(shí)行同源政策,只要服務(wù)器支持,就可以通過它進(jìn)行跨源通信。
CORS
CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請(qǐng)求,從而克服了AJAX只能同源使用的限制。
跨域資源共享( CORS )機(jī)制允許 Web 應(yīng)用服務(wù)器進(jìn)行跨域訪問控制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M(jìn)行。其需要服務(wù)端和客戶端同時(shí)支持。
跨域資源共享標(biāo)準(zhǔn)( cross-origin sharing standard )允許在下列場(chǎng)景中使用跨域 HTTP 請(qǐng)求:
由 XMLHttpRequest 或 Fetch 發(fā)起的跨域 HTTP 請(qǐng)求
Web 字體 (CSS 中通過 @font-face 使用跨域字體資源), 因此,網(wǎng)站就可以發(fā)布 TrueType 字體資源,并只允許已授權(quán)網(wǎng)站進(jìn)行跨站調(diào)用
WebGL 貼圖
使用 drawImage 將 Images/video 畫面繪制到 canvas
樣式表(使用 CSSOM)
Scripts (未處理的異常)
CORS 存在以下三種主要場(chǎng)景,分別是簡(jiǎn)單請(qǐng)求,預(yù)檢請(qǐng)求和附帶身份憑證的請(qǐng)求。
簡(jiǎn)單請(qǐng)求:若只使用 GET, HEAD 或者 POST 請(qǐng)求,且除 CORS 安全的首部字段集合外,無人為設(shè)置該集合之外的其他首部字段,同時(shí) Content-Type 值屬于下列之一,那么該請(qǐng)求則可以被視為簡(jiǎn)單請(qǐng)求:
application/x-www-form-urlencoded
multipart/form-data
text/plain
此情況下,若服務(wù)端返回的 Access-Control-Allow-Origin: * ,則表明該資源可以被任意外域訪問。若要指定僅允許來自某些域的訪問,需要將 * 設(shè)定為該域,例如:
Access-Control-Allow-Origin: http://foo.example
預(yù)檢請(qǐng)求:與前述簡(jiǎn)單請(qǐng)求不同,該要求必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)器,以獲知服務(wù)器是否允許該實(shí)際請(qǐng)求。當(dāng)請(qǐng)求滿足以下三個(gè)條件任意之一時(shí), 即應(yīng)首先發(fā)送預(yù)檢請(qǐng)求:
使用了 PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH 中任一的 HTTP 方法
人為設(shè)置了對(duì) CORS 安全的首部字段集合之外的其他首部字段
Content-Type 的值不屬于下列之一
application/x-www-form-urlencoded
multipart/form-data
text/plain
預(yù)檢請(qǐng)求完成之后(通過 OPTIONS 方法實(shí)現(xiàn)),才發(fā)送實(shí)際請(qǐng)求。一個(gè)示范 HTTP 請(qǐng)求如下所示:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?>Arun';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
附帶身份憑證的請(qǐng)求:這種方式的特點(diǎn)在于能夠在跨域請(qǐng)求時(shí)向服務(wù)器發(fā)送憑證請(qǐng)求,例如 Cookies (withCredentials 標(biāo)志設(shè)置為 true)。
一般而言,對(duì)于跨域 XMLHttpRequest 或 Fetch 請(qǐng)求,瀏覽器不會(huì)發(fā)送身份憑證信息。如果要發(fā)送憑證信息,需要設(shè)置 XMLHttpRequest 的某個(gè)特殊標(biāo)志位。但是需要注意的是,如果服務(wù)器端的響應(yīng)中未攜帶 Access-Control-Allow-Credentials: true,瀏覽器將不會(huì)把響應(yīng)內(nèi)容返回給請(qǐng)求的發(fā)送者。
附帶身份憑證的請(qǐng)求與通配符
對(duì)于附帶身份憑證的請(qǐng)求,服務(wù)器不得設(shè)置 Access-Control-Allow-Origin 的值為“*”。
這是因?yàn)檎?qǐng)求的首部中攜帶了 Cookie 信息,如果 Access-Control-Allow-Origin 的值為“*”,請(qǐng)求將會(huì)失敗。而將 Access-Control-Allow-Origin 的值設(shè)置為 http://foo.example,則請(qǐng)求將成功執(zhí)行。
另外,響應(yīng)首部中也攜帶了 Set-Cookie 字段,嘗試對(duì) Cookie 進(jìn)行修改。如果操作失敗,將會(huì)拋出異常。
MDN 引例如下:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;
invocation.onreadystatechange = handler;
invocation.send();
}
}
其實(shí)由上我們知道,CORS 的優(yōu)點(diǎn)也非常明顯:CORS支持所有類型的HTTP請(qǐng)求,是跨域HTTP請(qǐng)求的根本解決方案。
以上就是所有的跨域請(qǐng)求解決方案,根據(jù)實(shí)際生產(chǎn)環(huán)境,總有一款適合你。
參考
https://github.com/wengjq/Blog/issues/2
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
http://www.cnblogs.com/zichi/p/4620656.html
總結(jié)
以上是生活随笔為你收集整理的vue跨域解决方案websocket_前端跨域解决方案汇总的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: thinkphp 编辑器kindedit
- 下一篇: 【整理】MySQL 之 autocomm