跨域資源共享CORS總結(jié)

摘要:CORS是一個(gè)W3C標(biāo)準(zhǔn)窘问,全程是“跨域資源共享”(Cross-origin resource sharing)。CORS允許瀏覽器向跨源服務(wù)器發(fā)出XMLHttpRequest請(qǐng)求话侄,以克服AJAX只能基于同源策略的使用限制。本文將詳細(xì)介紹CORS的內(nèi)部機(jī)制。
1. 簡(jiǎn)介

??CORS需要瀏覽器與服務(wù)器同時(shí)支持杨赤。目前,所有瀏覽器都支持該功能截汪,IE瀏覽器不能低于IE10疾牲。
??整個(gè)CORS通信過程,都是瀏覽器自動(dòng)完成挫鸽,不需要用戶參與说敏。對(duì)于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別丢郊,代碼完全一樣盔沫。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息枫匾,有時(shí)還會(huì)多出一次附加的請(qǐng)求(如添加了Origin : http://localhost:63343架诞,Access-Control-Request-Headers : accept, content-type,Access-Control-Request-Method : POST)干茉,但是用戶不會(huì)有感覺谴忧。
??因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口或者約定沾谓,就可以跨源通信委造。

2.兩種請(qǐng)求

??瀏覽器把CORS請(qǐng)求分成:簡(jiǎn)單請(qǐng)求(Simple Request)和非簡(jiǎn)單請(qǐng)求(Not-so simple request)。
??只要同時(shí)滿足一下兩個(gè)條件均驶,就屬于簡(jiǎn)單請(qǐng)求昏兆。
??(1)請(qǐng)求方法是HEAD/GET/POST三種方法之一;
??(2)HTTP的頭信息不超出一下幾種字段:Accept/Accept-Encoding/Accept-Language/Cache-Control/Connection/Cookie/Host/If-Modified-Since/Referer/User-Agent/Content-Type/Content-Language妇穴。其中Content-Type僅限于三個(gè)值:application/x-www-form-urlencoded爬虱、multipart/form-data、text/pain腾它。
??凡是不滿足上面兩個(gè)條件跑筝,就屬于非簡(jiǎn)單請(qǐng)求。瀏覽器對(duì)于兩種請(qǐng)求的處理是不一樣的瞒滴。

3. 簡(jiǎn)單請(qǐng)求
3.1 基本流程

??對(duì)于簡(jiǎn)單請(qǐng)求曲梗,瀏覽器直接發(fā)出CORS請(qǐng)求,在HTTP HEADER中增加了Origin字段逛腿。如下:

POST /nlp/segment HTTP/1.1
Host: 10.1.222.80:8084
Connection: keep-alive
Content-Length: 90
Accept: */*; q=0.01
Origin: http://localhost:63343
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
Content-Type: application/ x-www-form-urlencoded;charset=UTF-8
Referer: http://localhost:63343/BZZZ/bz.html?_ijt=eu8vkf2u50ccl1gshpr4lhjt3r
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8

??上面的頭信息中稀并,Origin字段用來說明,本次請(qǐng)求來自哪個(gè)源(協(xié)議+域名+端口)单默。服務(wù)器根據(jù)這個(gè)值碘举,決定是否同意這次請(qǐng)求。
如果Origin制定的源不在許可范圍之內(nèi)搁廓,服務(wù)器會(huì)返回一個(gè)正常的HTTP回應(yīng)引颈。瀏覽器發(fā)現(xiàn)Http Response頭信息中沒有包含Access-Control-Allow-Origin字段,就判斷出錯(cuò)并拋出一個(gè)錯(cuò)誤境蜕,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲蝙场。注意,這種錯(cuò)誤無法通過狀態(tài)碼識(shí)別粱年,因?yàn)镠TTP回應(yīng)的狀態(tài)碼有可能是200.
??如果Origin制定的域名在許可范圍內(nèi)售滤,服務(wù)器返回的響應(yīng)中多出幾個(gè)頭信息字段:

Access-Control-Allow-Headers:Content-Type
Access-Control-Allow-Methods:GET,POST,PUT,OPTIONS,DELETE
Access-Control-Expose-Headers: FooBar
Access-Control-Allow-Origin:*
Content-Type:text/html; charset=UTF-8

??上面的頭信息中,有三個(gè)與CORS請(qǐng)求相關(guān)的字段台诗,均以Access-Control-開頭完箩。
(1). Access-Control-Allow-Origin
??該字段是必須的,它的值要么是請(qǐng)求時(shí)Origin字段的值拉队,要么是一個(gè)*弊知,表示接受任意域名的請(qǐng)求。
(2). Access-Control-Allow-Credentials
??該字段可選粱快,它是一個(gè)Bool值秩彤,表示是否允許發(fā)送Cookie叔扼。默認(rèn)情況下,Cookie不包括在CORS請(qǐng)求中漫雷,設(shè)為true瓜富,即表示服務(wù)器明確許可,Cookie可以包含在請(qǐng)求中珊拼,一起發(fā)給服務(wù)器食呻。這個(gè)值只能設(shè)為true流炕,沒有false取值澎现,如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可每辟。
(3). Access-Control-Allow-Methods
??該字段可選剑辫,如果允許跨域請(qǐng)求的方法是PUT/GET/POST/OPTIONS/DELETE等其中的一項(xiàng)或幾項(xiàng),就需要設(shè)置該字段的值為”PUT,GET,POST,OPTIONS”渠欺。
(4). Access-Control-Expose-Headers
??該字段可選妹蔽,CORS請(qǐng)求時(shí),XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到6哥基本字段:Cache-Control挠将、Content-Language胳岂、Content-Type、Expires舔稀、Last-Modified乳丰、Pragma。如果想拿到其他字段内贮,就必須在Access-Control-Expose-Headers字段中指定产园。上面的例子制定,getResponseHeader(’FooBar’)可以返回FooBar字段的值夜郁。
(5). Access-Control-Allow-Headers
??該字段可選什燕,CORS請(qǐng)求時(shí),如果是自定義Http Request Header竞端,就必須在該字段中指定對(duì)應(yīng)的Header Name屎即,如例子中自定義Header為’Test’,那么需要在服務(wù)器端制定該字段為’Test’事富。

3.2 withCredentials屬性

??上面提到CORS請(qǐng)求默認(rèn)不發(fā)送Cookie和HTTP認(rèn)證信息技俐。如果要把Cookie發(fā)到服務(wù)器,一方面要服務(wù)器同意赵颅,指定Access-Control-Allow-Credentials字段虽另。

Access-Control-Allow-Credentials: true

??另外一方面,開發(fā)者必須在AJAX請(qǐng)求中打開withCredentials屬性饺谬。如下:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

??否則捂刺,即使服務(wù)器同意發(fā)送Cookie谣拣,瀏覽器也不會(huì)發(fā)送∽逭梗或者服務(wù)器要求設(shè)置Cookie森缠,瀏覽器也不會(huì)處理。如果省略withCredentials字段設(shè)置仪缸,有些瀏覽器還是會(huì)一起發(fā)送Cookie贵涵。這時(shí)需要顯示關(guān)閉withCredentials。
xhr.withCredentials = false;
??需要注意的是恰画,如果發(fā)送Cookie宾茂,Access-Control-Allow-Origin就不能設(shè)為”*”,必須制定明確的拴还、與請(qǐng)求網(wǎng)頁一直的域名跨晴。同時(shí)Cookie依然遵循同源側(cè)策略,只有用服務(wù)器域名設(shè)置設(shè)置的Cookie才會(huì)上傳片林,其他域名的Cookie并不會(huì)上傳端盆,且(跨源)發(fā)起請(qǐng)求的網(wǎng)頁代碼中document.cookie無法讀取服務(wù)器域名下的Cookie。

4. 非簡(jiǎn)單請(qǐng)求
4.1 預(yù)檢請(qǐng)求

??非簡(jiǎn)單請(qǐng)求是對(duì)服務(wù)器有特殊要求的請(qǐng)求费封,比如請(qǐng)求方法是PUT/DELETE焕妙,或者Content-Type字段的類型是application/json。
非簡(jiǎn)單請(qǐng)求的CORS請(qǐng)求弓摘,會(huì)在正式itongxin之前焚鹊,增加愛一次HTTP查詢請(qǐng)求,叫預(yù)檢請(qǐng)求(preflight)衣盾。
??瀏覽器現(xiàn)詢問服務(wù)器寺旺,當(dāng)前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單中,以及可以使用哪些HTTP動(dòng)詞和頭信息字段势决。只有得到肯定答復(fù)阻塑,瀏覽器才會(huì)發(fā)出正式的XMLHttpRequest請(qǐng)求,否則就報(bào)錯(cuò)果复。下面是一段瀏覽器的JS腳本:

var test1 = {method:"segment", format:"json", message:["我是中國人陈莽!","歡迎來到中國"]};
$.ajax({
            url : "http://10.1.222.80:8084/nlp/segment",
            type : "post",
            dataType : "json",
            contentType:"application/json;charset=utf-8",
            data :JSON.stringify(test1),
            crossDomain: true,
            /*headers:{
              "Content-Type":"application/x-www-form-urlencoded"
            },*/
            timeout : 10000 
});

??上面的請(qǐng)求中是Content-Type為application/json的請(qǐng)求。瀏覽器認(rèn)為這是一個(gè)非簡(jiǎn)單請(qǐng)求虽抄,自動(dòng)向服務(wù)器發(fā)送一個(gè)預(yù)檢請(qǐng)求走搁,要求服務(wù)器確認(rèn)可以這樣請(qǐng)求。下面是預(yù)檢請(qǐng)求的HTTP頭信息:

OPTIONS /nlp/segment HTTP/1.1
Host: 10.1.222.80:8084
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:63343
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://localhost:63343/BZZZ/bz.html?_ijt=eu8vkf2u50ccl1gshpr4lhjt3r
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

??預(yù)檢請(qǐng)求使用的請(qǐng)求方法是OPTIONS迈窟,表示這個(gè)請(qǐng)求用來詢問私植。頭信息中關(guān)鍵字段是Origin,表示請(qǐng)求來自哪個(gè)源车酣。
除了Origin字段曲稼,預(yù)檢請(qǐng)求的頭信息還包括兩個(gè)特殊字段:
??(1). Access-Control-Request-Method
??該字段是必須的索绪,用來列出瀏覽器的CORS請(qǐng)求會(huì)用到哪些HTTP方法,例子中是POST贫悄。
??(2). Access-Control-Request-Headers
??該字段是都好分割的字符串瑞驱,指定瀏覽器CORS請(qǐng)求會(huì)額外發(fā)送的頭信息字段,上面的例子默認(rèn)application/json對(duì)應(yīng)的額外字段是”Content-Type”窄坦。

4.2 預(yù)檢請(qǐng)求的回應(yīng)Response

??服務(wù)器收到預(yù)檢請(qǐng)求后唤反,檢查Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后鸭津,確認(rèn)允許跨源請(qǐng)求彤侍,并做出回應(yīng)。如下:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Connection: close
Time-Used: 25ms
Content-Length-UnGzipped: 7
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,OPTIONS,DELETE
Access-Control-Allow-Headers: Content-Type
Content-Encoding: gzip
Content-Length: 33

??上面的Http Response中曙博,關(guān)鍵是Access-Control-Allow-Origin字段拥刻,表示http://localhost:63343可以請(qǐng)求數(shù)據(jù),該字段可以設(shè)為”*”父泳,表示同意任意跨源請(qǐng)求。
??如果服務(wù)器否定了預(yù)檢請(qǐng)求吴汪,會(huì)返回一個(gè)正常的HTTP Response惠窄,但是沒有任何CORS相關(guān)的頭信息字段。這時(shí)候?yàn)g覽器認(rèn)為i額服務(wù)器不同意預(yù)檢請(qǐng)求漾橙,因此出發(fā)一個(gè)錯(cuò)誤杆融,被XMLHttpRequest對(duì)象的onerror毀掉函數(shù)捕獲∷耍控制臺(tái)會(huì)打印出如下報(bào)錯(cuò)信息脾歇。

XMLHttpRequest cannot load http://localhost:63343.
Origin http://localhost:63343 is not allowed by Access-Control-Allow-Origin.

??服務(wù)器響應(yīng)的CORS其他相關(guān)字段如下:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,OPTIONS,DELETE
Access-Control-Allow-Headers: Content-Type

??(1). Access-Control-Allow-Methods
??該字段是必需的,其取值是逗號(hào)分割的字符串淘捡,表明服務(wù)器支持的所有跨域請(qǐng)求的方法藕各。注意,返回的是所有支持的方法焦除,而不單是瀏覽器請(qǐng)求的哪個(gè)方法激况。這個(gè)是為了避免多次預(yù)檢請(qǐng)求。
??(2). Access-Control-Allow-Headers
??如果瀏覽器請(qǐng)求包括Access-Control-Allow-Headers字段膘魄,則該字段是必須的乌逐,其取值是一個(gè)逗號(hào)分割的字符串,表明服務(wù)器支持的所有頭信息字段创葡,不限于瀏覽器在預(yù)檢中請(qǐng)求的字段浙踢。
??(3). Access-Control-Allow-Credentials
??該字段與簡(jiǎn)單請(qǐng)求時(shí)字段含義相同。
??(4). Access-Control-Max-Age
??該字段可選灿渴,用來制定本次預(yù)檢請(qǐng)求的有效期洛波,單位為秒呐芥。緩存于瀏覽器中,在有效期中奋岁,不需要發(fā)出另一條預(yù)檢請(qǐng)求思瘟。

4.3 瀏覽器的正常請(qǐng)求和回應(yīng)

??一旦服務(wù)器通過了預(yù)檢請(qǐng)求,以后每次瀏覽器正常的CORS請(qǐng)求就都與簡(jiǎn)單請(qǐng)求一樣闻伶,包括Origin字段信息滨攻。服務(wù)器的回應(yīng)也會(huì)有Access-Control-Allow-Origin字段。

5. 與JSONP比較

??CORS與Jsonp使用目的相同蓝翰,但是Jsonp只支持GET請(qǐng)求光绕,CORS支持所有類型的HTTP請(qǐng)求。JSONP的有時(shí)在于支持老式瀏覽器畜份,以及向不支持CORS的網(wǎng)站請(qǐng)求數(shù)據(jù)诞帐。


黃博博
2016年8月2日

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市爆雹,隨后出現(xiàn)的幾起案子停蕉,更是在濱河造成了極大的恐慌,老刑警劉巖钙态,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慧起,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡册倒,警方通過查閱死者的電腦和手機(jī)蚓挤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驻子,“玉大人灿意,你說我怎么就攤上這事〕绾牵” “怎么了缤剧?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)演熟。 經(jīng)常有香客問我鞭执,道長(zhǎng),這世上最難降的妖魔是什么芒粹? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任兄纺,我火速辦了婚禮,結(jié)果婚禮上化漆,老公的妹妹穿的比我還像新娘估脆。我一直安慰自己,他們只是感情好座云,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布疙赠。 她就那樣靜靜地躺著付材,像睡著了一般。 火紅的嫁衣襯著肌膚如雪圃阳。 梳的紋絲不亂的頭發(fā)上厌衔,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音捍岳,去河邊找鬼富寿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锣夹,可吹牛的內(nèi)容都是我干的页徐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼银萍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼变勇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贴唇,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤搀绣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后滤蝠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豌熄,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年物咳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹄皱。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡览闰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巷折,到底是詐尸還是另有隱情压鉴,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布锻拘,位于F島的核電站油吭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏署拟。R本人自食惡果不足惜婉宰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望推穷。 院中可真熱鬧心包,春花似錦、人聲如沸馒铃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至娃殖,卻和暖如春值戳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背炉爆。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工堕虹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叶洞。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓鲫凶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親衩辟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子螟炫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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

  • CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)艺晴。 ...
    起名字太累閱讀 946評(píng)論 0 2
  • CORS是一個(gè)W3C標(biāo)準(zhǔn)昼钻,全稱是"跨域資源共享"(Cross-origin resource sharing)。 ...
    myccy8閱讀 439評(píng)論 0 2
  • CORS是一個(gè)W3C標(biāo)準(zhǔn)封寞,全稱是"跨域資源共享"(Cross-origin resource sharing)然评。 ...
    奇特思維家閱讀 1,121評(píng)論 0 3
  • 轉(zhuǎn)載于阮一峰老師的跨域資源共享 CORS 詳解 CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-or...
    俊_杰閱讀 442評(píng)論 0 3
  • 于國內(nèi)的品牌和中小企業(yè)來說狈究,網(wǎng)絡(luò)視頻營銷還只停留在概念普及階段碗淌。而從國外的營銷趨勢(shì)中看,視頻營銷在品牌整合營銷策略...
    做自己的CEO閱讀 792評(píng)論 1 6