關于跨域資料的整理

什么是跨域姓建?

概念:指從一個域名的網(wǎng)頁向另一個網(wǎng)頁去請求資源诞仓,只要協(xié)議、域名速兔、端口有任何一個不同墅拭,都被當作是跨域。例如:

url 說明 是否允許通信
http://www.a.com/a.js
http://www.a.com/b.js
相同協(xié)議涣狗,同一域名 允許
https://www.a.com/a.js
http://www.a.com/a.js
不同協(xié)議谍婉,同一域名 不允許
http://www.a.com/a.js
http://www.b.com/a.js
相同協(xié)議舒憾,不同域名 不允許
http://js.a.com/a.js
http://www.b.com/a.js
相同協(xié)議,不同域名 不允許
http://www.a.com:8080/a.js
http://www.a.com/b.js
主域相同屡萤,子域不同 不允許
http://www.a.com/a.js
http://70.23.92.75/a.js
域名和域名對應ip 不允許

為什么瀏覽器要限制跨域訪問呢珍剑?

本質(zhì)上來說是瀏覽器的同源策略所限制的掸宛,主要是防止惡意網(wǎng)頁訪問私人信息或損害用戶的數(shù)據(jù)死陆。

為什么要跨域?

既然跨域訪問存在安全問題唧瘾,那為什么又要跨域呢措译?因為有時候公司內(nèi)部會有多個不同子域,比如 http://www.a.comhttp://www.b.com, 從 http://www.a.com 訪問 http://www.b.com 的資源就會跨域饰序。還有一種情況就是調(diào)用一些外部的API领虹,也需要跨域。

如何跨域求豫?

首先糾正一個誤區(qū)塌衰,跨域并非瀏覽器限制了發(fā)起跨站請求的這種能力,恰恰相反蝠嘉,我們可以發(fā)出請求最疆,服務端也可以接收到請求并正常返回數(shù)據(jù),只不過在返回之后瀏覽器會阻止非同源數(shù)據(jù)(response)蚤告,從而在控制臺打出一系列報錯信息努酸。

主要有5種跨域方法,我們會分別介紹其工作原理并舉例說明杜恰,最后對比各個方法的優(yōu)缺點获诈。

1 JSON-P

JSONPJSON with Padding)是數(shù)據(jù)格式JSON的一種“使用模式”,可以讓網(wǎng)頁從別的網(wǎng)域要數(shù)據(jù)心褐,它的工作原理在于script標簽不受同源策略限制舔涎,并且請求得到script資源后會立即執(zhí)行。

  • 瀏覽器端

首先在瀏覽器端注冊一個回調(diào)函數(shù)逗爹,它的參數(shù)是期望服務器端返回的數(shù)據(jù)终抽,這個回調(diào)函數(shù)具體內(nèi)容就是處理這些數(shù)據(jù)。

function show(callback) {
        //處理數(shù)據(jù)
        }

然后動態(tài)地添加script標簽桶至,src地址為:請求資源的地址+回調(diào)函數(shù)名稱昼伴,這里的回調(diào)函數(shù)名稱是與服務器端約定好的。

$('#change').addEventListener('click',function(){
           var script=document.createElement('script')
            script.src='http://localhost:8080/getMusic?callback=show'
            document.head.appendChild(script)
            document.head.removeChild(script)
       })
  • 服務器端

首先從url中獲取回調(diào)函數(shù)名稱镣屹,看是否存在回調(diào)函數(shù)圃郊,如果存在的話,會動態(tài)的生成JavaScript代碼片段(例如show([data1,data2……]))女蜈,然后發(fā)送數(shù)據(jù)持舆。

    var cb=req.query.callback
    if (cb) {
        res.send(cb+'('+JSON.stringify(data)+')')
    } else {
        res.send(data)
    }
  • 執(zhí)行

瀏覽器端收到返回的數(shù)據(jù)色瘩,作為參數(shù)傳入回調(diào)函數(shù)show(),然后立即執(zhí)行這個JavaScript逸寓,這樣就能根據(jù)之前寫好的回調(diào)函數(shù)處理這些數(shù)據(jù)居兆。
完整代碼戳這里 ==> JSON-P

2 CORS

CORS全稱是跨域資源共享(Cross-origin resource sharing),是一種ajax跨域請求資源的方式竹伸,支持現(xiàn)代瀏覽器泥栖,IE支持10以上。



CROS實現(xiàn)方式很簡單勋篓,當你使用XMLHttpRequest發(fā)送請求時吧享,瀏覽器發(fā)現(xiàn)該請求 不符合同源策略,會給該請求加一個請求頭:Origin,后臺進行一系列處理譬嚣,如果確定接受請求則在返回結(jié)果中加一個響應頭:Access-Control-Allow-Origin;瀏覽器判斷該相應頭中是否包含Origin的值钢颂,如果有,瀏覽器就會處理響應拜银,我們就可以拿到響應數(shù)據(jù)殊鞭,如果不包含瀏覽器直接駁回,這是我們無法拿到響應數(shù)據(jù)尼桶,所以使用 CORS 跨域的時候其實和普通的 ajax 過程是一樣的操灿,只是瀏覽器在發(fā)現(xiàn)這是一個跨域請求的時候會自動幫我們處理一些事,比如驗證等等疯汁,所以說只要服務端提供支持牲尺,前端是不需要做額外的事情的。

var data =[];
    for (var i = 0; i < 5; i++) {
        var index = parseInt(Math.random()*musicList.length)
        data.push(musicList[index])
        musicList.splice(index,1)
    }
    res.header("Access-Control-Allow-Origin", "*")
    res.send(data);

只需要在服務器端加上Access-Control-Allow-Origin幌蚊,它的值是請求時Origin字段的值或者 *谤碳,*表示接受任意域名的請求。
完整代碼戳這里 ==> CROS

2種請求(轉(zhuǎn)載自https://zhuanlan.zhihu.com/p/24198444)

  • 簡單請求

若請求滿足所有下述條件溢豆,則該請求可視為“簡單請求”:

  • 使用下列方法之一:
    GET
    HEAD
    POST

  • HTTP的頭信息不超出以下幾種字段:
    Accept
    Accept-Language
    Content-Language
    Content-Type
    DRP
    Downlink
    Save-Data
    Viewport-Width
    Width

  • Content-Type的值屬于下列之一:
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain
    簡單請求不會觸發(fā) CROS預檢請求
    過程:

對于簡單的跨域請求蜒简,瀏覽器會自動在請求的頭信息加上 Origin 字段,表示本次請求來自哪個源(協(xié)議 + 域名 + 端口)漩仙,服務端會獲取到這個值搓茬,然后判斷是否同意這次請求并返回。

// 請求
GET /cors HTTP/1.1
Origin: https://api.qiutc.me
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

情況一:服務端允許

如果服務端許可本次請求队他,就會在返回的頭信息多出幾個字段:

// 返回
Access-Control-Allow-Origin: https://api.qiutc.me
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Info
Content-Type: text/html; charset=utf-8

這三個帶有 Access-Control 開頭的字段分別表示:

  • Access-Control-Allow-Origin:必須卷仑,它的值是請求時Origin字段的值或者 **表示接受任意域名的請求麸折。
  • Access-Control-Allow-Credentials:可選锡凝,它的值是一個布爾值,表示是否允許發(fā)送Cookie垢啼。默認情況下窜锯,Cookie不包括在CORS請求之中张肾。設為true,即表示服務器明確許可锚扎,Cookie可以包含在請求中吞瞪,一起發(fā)給服務器。
    再需要發(fā)送cookie的時候還需要注意要在AJAX請求中打開 withCredentials 屬性:
var xhr = new XMLHttpRequest(); 
xhr.withCredentials = true;

需要注意的是驾孔,如果要發(fā)送Cookie芍秆,Access-Control-Allow-Origin就不能設為*,必須指定明確的助币、與請求網(wǎng)頁一致的域名浪听。同時螟碎,Cookie依然遵循同源政策眉菱,只有用服務器域名設置的Cookie才會上傳,其他域名的Cookie并不會上傳掉分,且原網(wǎng)頁代碼中的document.cookie也無法讀取服務器域名下的Cookie俭缓。

  • Access-Control-Expose-Headers: 可選。CORS請求時酥郭,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control华坦、Content-Language、Content-Type不从、Expires惜姐、Last-Modified、Pragma椿息。如果想拿到其他字段歹袁,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定寝优,getResponseHeader('Info')可以返回Info字段的值条舔。

情況二:服務端拒絕

當然我們?yōu)榱朔乐菇涌诒粊y調(diào)用,需要限制源乏矾,對于不允許的源孟抗,服務端還是會返回一個正常的HTTP回應,但是不會帶上 Access-Control-Allow-Origin 字段钻心,瀏覽器發(fā)現(xiàn)這個跨域請求的返回頭信息沒有該字段凄硼,就會拋出一個錯誤,會被 XMLHttpRequestonerror 回調(diào)捕獲到捷沸。

這種錯誤無法通過 HTTP 狀態(tài)碼判斷摊沉,因為回應的狀態(tài)碼有可能是200。

  1. 非簡單請求

條件:除了簡單請求以外的CORS請求亿胸。

非簡單請求是那種對服務器有特殊要求的請求坯钦,比如請求方法是PUT或DELETE预皇,或者Content-Type字段的類型是 application/json。

過程:

1)預檢請求

非簡單請求的CORS請求婉刀,會在正式通信之前吟温,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)突颊。

瀏覽器先詢問服務器鲁豪,當前網(wǎng)頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段律秃。只有得到肯定答復爬橡,瀏覽器才會發(fā)出正式的XMLHttpRequest請求,否則就報錯棒动。

預檢請求的發(fā)送請求:

OPTIONS /cors HTTP/1.1
Origin: https://api.qiutc.me
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.qiutc.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

"預檢"請求用的請求方法是OPTIONS糙申,表示這個請求是用來詢問的。頭信息里面船惨,關鍵字段是Origin柜裸,表示請求來自哪個源。

除了Origin字段粱锐,"預檢"請求的頭信息包括兩個特殊字段疙挺。

  • Access-Control-Request-Method
    該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法怜浅,上例是PUT铐然。

  • Access-Control-Request-Headers
    該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發(fā)送的頭信息字段恶座,上例是X-Custom-Header搀暑。

預檢請求的返回:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: https://api.qiutc.me
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
  • Access-Control-Allow-Methods

    必需,它的值是逗號分隔的一個字符串奥裸,表明服務器支持的所有跨域請求的方法险掀。注意,返回的是所有支持的方法湾宙,而不單是瀏覽器請求的那個方法樟氢。這是為了避免多次"預檢"請求。

  • Access-Control-Allow-Headers
    如果瀏覽器請求包括Access-Control-Request-Headers字段侠鳄,則Access-Control-Allow-Headers字段是必需的埠啃。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段伟恶,不限于瀏覽器在"預檢"中請求的字段碴开。

  • Access-Control-Max-Age
    該字段可選,用來指定本次預檢請求的有效期,單位為秒潦牛。上面結(jié)果中眶掌,有效期是20天(1728000秒),即允許緩存該條回應1728000秒(即20天)巴碗,在此期間朴爬,不用發(fā)出另一條預檢請求。

2)瀏覽器的正常請求和回應

一旦服務器通過了"預檢"請求橡淆,以后每次瀏覽器正常的CORS請求召噩,就都跟簡單請求一樣,會有一個Origin頭信息字段逸爵。服務器的回應具滴,也都會有一個Access-Control-Allow-Origin頭信息字段。

3 document.domain

瀏覽器的同源策略师倔,其限制之一就是我們說的不能通過ajax的方法去請求不同源中的資源构韵。 它的第二個限制是瀏覽器中不同域的頁面框架(frame)之間是不能進行js的交互操作的。

// 當前頁面域名:http://a.mac.com:8080/a.html
<div class="ct">
  <h1>使用降域?qū)崿F(xiàn)跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.mac.com/a.html">
  </div>

  <iframe src="http://b.mac.com/b.html" frameborder="0" ></iframe>

</div>

不同域的頁面框架可以獲取彼此的window對象溯革,但是無法獲取屬性和值贞绳。這個時候document.domian就有用了谷醉,只需要將a.mac.com 和b.mac.com 的document.domain都設置為相同的mac.com就可以了致稀。

需要注意的是:document.domain的設置是有限制的,我們只能把document.domian設置為自身或者更高一級的父域俱尼,并且主域必須相同抖单。

4 POSTmessage

window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可以使用它來向其它的window對象發(fā)送消息遇八,無論這個window對象是屬于同源或不同源矛绘。兼容性:


使用方法:

//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
    console.log(this.value);
    window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
        $('.main input').value = e.data
    console.log(e.data);
});

該方法的第一個參數(shù)message為要發(fā)送的消息,類型只能為字符串刃永;第二個參數(shù)targetOrigin用來限定接收消息的那個window對象所在的域货矮,如果不想限定域,可以使用通配符 * 斯够。

各種方法優(yōu)缺點比較

方法 優(yōu)點 缺點
JSONP 兼容性更好,不需要XMLHttpRequest或ActiveX的支持 只能通過GET方式請求囚玫,一方面是參數(shù)長度有限制,二是安全性比較差读规;后端需要知道前端的cb是什么樣的結(jié)構(gòu)抓督,主要在參數(shù)和回調(diào)名;后端需要進行參數(shù)和cb的拼接然后才能執(zhí)行束亏;
cros 前端比較方便铃在,只需要發(fā)送請求即可;安全性能夠得以控制和保障 兼容性不全面,需要做降級處理
document.domain 可以實現(xiàn)不同window之間的相互訪問和操作 只適用于父子window之間的通信定铜,不能用于xhr阳液;只能在主域相同且子域不同的情況下使用
postMessage 不需要后端介入,簡單快捷揣炕,一個函數(shù)外加兩個參數(shù)(請求url趁舀,發(fā)送數(shù)據(jù))就可以搞定 無法做到一對一的傳遞方式:監(jiān)聽中需要做很多消息的識別,由于postMessage發(fā)出的消息對于同一個頁面的不同功能相當于一個廣播的過程祝沸,該頁面的所有onmessage都會收到矮烹,所以需要做消息的判斷;
安全性問題:三方可以通過截獲罩锐,注入html或者腳本的形式監(jiān)聽到消息奉狈,從而能夠做到篡改的效果,所以在postMessage和onmessage中一定要做好這方面的限制涩惑;
發(fā)送的數(shù)據(jù)會通過結(jié)構(gòu)化克隆算法進行序列化仁期,所以只有滿足該算法要求的參數(shù)才能夠被解析,否則會報錯竭恬,如function就不能當作參數(shù)進行傳遞跛蛋;
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痊硕,隨后出現(xiàn)的幾起案子赊级,更是在濱河造成了極大的恐慌,老刑警劉巖岔绸,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件理逊,死亡現(xiàn)場離奇詭異,居然都是意外死亡盒揉,警方通過查閱死者的電腦和手機晋被,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刚盈,“玉大人羡洛,你說我怎么就攤上這事∨菏” “怎么了欲侮?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谴分。 經(jīng)常有香客問我锈麸,道長,這世上最難降的妖魔是什么牺蹄? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任忘伞,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氓奈。我一直安慰自己翘魄,他們只是感情好,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布舀奶。 她就那樣靜靜地躺著暑竟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪育勺。 梳的紋絲不亂的頭發(fā)上但荤,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音涧至,去河邊找鬼腹躁。 笑死,一個胖子當著我的面吹牛南蓬,可吹牛的內(nèi)容都是我干的纺非。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼赘方,長吁一口氣:“原來是場噩夢啊……” “哼烧颖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起窄陡,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤炕淮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泳梆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳖悠,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年优妙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憎账。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡套硼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胞皱,到底是詐尸還是另有隱情邪意,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布反砌,位于F島的核電站雾鬼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宴树。R本人自食惡果不足惜策菜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧又憨,春花似錦翠霍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躏将,卻和暖如春锄弱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祸憋。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工棵癣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夺衍。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓狈谊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沟沙。 傳聞我的和親對象是個殘疾皇子河劝,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容