CORS 為什么要區(qū)分『簡(jiǎn)單請(qǐng)求』和『預(yù)檢請(qǐng)求』

原文

CORS(cross-origin resource sharing)厌衙,跨源資源共享(一般俗稱(chēng)『跨域請(qǐng)求』),想必大家都已經(jīng)有基本的了解。如果你還不了解的話构挤,可以閱讀MDN 上的介紹 (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) ,這里就不贅述了惕鼓。

不過(guò)在學(xué)習(xí)CORS時(shí)筋现,有些朋友會(huì)有疑惑,為什么CORS要把請(qǐng)求分成兩類(lèi):簡(jiǎn)單請(qǐng)求和預(yù)檢請(qǐng)求(preflighted requests)呢箱歧?

如果我們看簡(jiǎn)單請(qǐng)求和預(yù)檢請(qǐng)求的區(qū)分矾飞,會(huì)看到有很多的條件:

簡(jiǎn)單請(qǐng)求的 HTTP 方法只能是 GET、HEAD 或 POST

簡(jiǎn)單請(qǐng)求的 HTTP 頭只能是 Accept/Accept-Language/Conent-Language/Content-Type 等

簡(jiǎn)單請(qǐng)求的 Content-Type 頭只能是 text/plain呀邢、multipart/form-data 或 application/x-www-form-urlencoded

看上去很是復(fù)雜洒沦。

那么怎么理解這些限制呢?

其實(shí)驼鹅,簡(jiǎn)單請(qǐng)求就是普通 HTML Form 在不依賴(lài)腳本的情況下可以發(fā)出的請(qǐng)求微谓,比如表單的 method 如果指定為 POST 森篷,可以用 enctype 屬性指定用什么方式對(duì)表單內(nèi)容進(jìn)行編碼,合法的值就是前述這三種豺型。

非簡(jiǎn)單請(qǐng)求就是普通 HTML Form 無(wú)法實(shí)現(xiàn)的請(qǐng)求仲智。比如 PUT 方法、需要其他的內(nèi)容編碼方式姻氨、自定義頭之類(lèi)的钓辆。

對(duì)于服務(wù)器來(lái)說(shuō),第一肴焊,許多服務(wù)器壓根沒(méi)打算給跨源用前联。當(dāng)然你不給 CORS 響應(yīng)頭,瀏覽器也不會(huì)使用響應(yīng)結(jié)果娶眷,但是請(qǐng)求本身可能已經(jīng)造成了后果似嗤。所以最好是默認(rèn)禁止跨源請(qǐng)求。

第二届宠,要回答某個(gè)請(qǐng)求是否接受跨源烁落,可能涉及額外的計(jì)算邏輯。這個(gè)邏輯可能很簡(jiǎn)單豌注,比如一律放行伤塌。也可能比較復(fù)雜,結(jié)果可能取決于哪個(gè)資源哪種操作來(lái)自哪個(gè) origin轧铁。對(duì)瀏覽器來(lái)說(shuō)每聪,就是某個(gè)資源是否允許跨源這么簡(jiǎn)單;對(duì)服務(wù)器來(lái)說(shuō)齿风,計(jì)算成本卻可大可小药薯。所以我們希望最好不用每次請(qǐng)求都讓服務(wù)器勞神計(jì)算。

CORS-preflight 就是這樣一種機(jī)制救斑,瀏覽器先單獨(dú)請(qǐng)求一次果善,詢(xún)問(wèn)服務(wù)器某個(gè)資源是否可以跨源,如果不允許的話就不發(fā)實(shí)際的請(qǐng)求系谐。注意先許可再請(qǐng)求等于默認(rèn)禁止了跨源請(qǐng)求巾陕。如果允許的話,瀏覽器會(huì)記住纪他,然后發(fā)實(shí)際請(qǐng)求鄙煤,且之后每次就都直接請(qǐng)求而不用再詢(xún)問(wèn)服務(wù)器否可以跨源了。于是茶袒,服務(wù)器想支持跨源梯刚,就只要針對(duì) preflight 進(jìn)行跨源許可計(jì)算。本身真正的響應(yīng)代碼則完全不管這個(gè)事情薪寓。并且因?yàn)?preflight 是許可式的亡资,也就是說(shuō)如果服務(wù)器不打算接受跨源澜共,什么事情都不用做。

但是這機(jī)制只能限于非簡(jiǎn)單請(qǐng)求锥腻。在處理簡(jiǎn)單請(qǐng)求的時(shí)候嗦董,如果服務(wù)器不打算接受跨源請(qǐng)求,不能依賴(lài) CORS-preflight 機(jī)制瘦黑。因?yàn)椴煌ㄟ^(guò) CORS京革,普通表單也能發(fā)起簡(jiǎn)單請(qǐng)求,所以默認(rèn)禁止跨源是做不到的幸斥。

既然如此匹摇,簡(jiǎn)單請(qǐng)求發(fā) preflight 就沒(méi)有意義了,就算發(fā)了服務(wù)器也省不了后續(xù)每次的計(jì)算甲葬,反而在一開(kāi)始多了一次 preflight廊勃。

有些人把簡(jiǎn)單請(qǐng)求不需要 preflight 理解為『向下兼容』。這也不能說(shuō)錯(cuò)经窖。但嚴(yán)格來(lái)說(shuō)供搀,并不是『為了向下兼容』而不能發(fā)。理論上瀏覽器可以區(qū)別對(duì)待表單請(qǐng)求和非表單請(qǐng)求 —— 對(duì)傳統(tǒng)的跨源表單提交不發(fā) preflight钠至,從而保持兼容,只對(duì)非表單跨源請(qǐng)求發(fā) preflight胎源。

但這樣做并沒(méi)有什么好處棉钧,反而把事情搞復(fù)雜了。比如本來(lái)你可以直接用腳本發(fā)跨源普通請(qǐng)求涕蚤,盡管(在服務(wù)器默認(rèn)沒(méi)有跨源處理的情況下)你無(wú)法得到響應(yīng)結(jié)果宪卿,但是你的需求可能只是發(fā)送無(wú)需返回,比如打個(gè)日志万栅。但現(xiàn)在如果服務(wù)器不理解 preflight 你就干不了這個(gè)事情了佑钾。

而且如果真的這樣做,服務(wù)器就變成了默認(rèn)允許跨源表單烦粒,如果想控制跨源休溶,還是得(跟原本一樣)直接在響應(yīng)處理中執(zhí)行跨源計(jì)算邏輯;另一方面服務(wù)器又需要增加對(duì) preflight 請(qǐng)求的響應(yīng)支持扰她,執(zhí)行類(lèi)似的跨源計(jì)算邏輯以控制來(lái)自非表單的相同跨源請(qǐng)求兽掰。服務(wù)器通常沒(méi)有區(qū)分表單/非表單差異的需求,這樣搞純粹是折騰服務(wù)器端工程師徒役。

所以簡(jiǎn)單請(qǐng)求不發(fā) preflight 不是因?yàn)椴荒芗嫒菽蹙。且驗(yàn)榧嫒莸那疤嵯掳l(fā) preflight 對(duì)絕大多數(shù)服務(wù)器應(yīng)用來(lái)說(shuō)沒(méi)有意義,反而把問(wèn)題搞復(fù)雜了忧勿。

  • 補(bǔ)充1
    賀師俊 (作者) :
    絕對(duì)意義上的后端安全是另一個(gè)層面的事情杉女,不要混在一起理解瞻讽。簡(jiǎn)單請(qǐng)求不存在『繞過(guò)』的問(wèn)題。從有網(wǎng)站開(kāi)始熏挎,簡(jiǎn)單請(qǐng)求就一直可以跨源提交速勇。所以服務(wù)器如果要禁止簡(jiǎn)單請(qǐng)求的跨源提交,從來(lái)就是要自己處理的婆瓜。而節(jié)省跨源計(jì)算只能針對(duì)新的需求快集,也就是原本瀏覽器不可能發(fā)送的跨源非簡(jiǎn)單請(qǐng)求。你確實(shí)可以把簡(jiǎn)單請(qǐng)求不需要preflight理解為『為了向下兼容』廉白。但嚴(yán)格來(lái)說(shuō)个初,不是。理論上瀏覽器可以對(duì)傳統(tǒng)的跨源表單提交不發(fā)preflight猴蹂,從而保持兼容院溺,只對(duì)腳本發(fā)起的跨源表單提交發(fā)preflight。這樣服務(wù)器這里默認(rèn)允許跨源表單提交磅轻,但通過(guò)響應(yīng)preflight來(lái)控制腳本的相同跨源請(qǐng)求珍逸。但是服務(wù)器通常沒(méi)有區(qū)分這種微秒差異的需求。所以不發(fā)preflight不是因?yàn)椴荒芗嫒萘铮且驗(yàn)榧嫒莸那疤嵯掳l(fā)preflight對(duì)絕大多數(shù)服務(wù)器應(yīng)用來(lái)說(shuō)沒(méi)有意義谆膳。

*補(bǔ)充2
Ivony:
如果我們現(xiàn)在重新設(shè)計(jì)整個(gè)HTTP協(xié)議,我們可以要求瀏覽器在發(fā)送任何數(shù)據(jù)到另外一個(gè)域的服務(wù)器之前撮躁,都必須先發(fā)送preflight request漱病。但是大部分現(xiàn)存網(wǎng)站并未針對(duì)preflight request做出實(shí)現(xiàn),所以這意味著現(xiàn)有的互聯(lián)網(wǎng)中把曼,如果一個(gè)域的表單向另一個(gè)域提交的時(shí)候會(huì)跨域失敗杨帽,直到目標(biāo)網(wǎng)站更新處理perflight request為止。所以在我們制定這一新的標(biāo)準(zhǔn)的時(shí)候嗤军,應(yīng)當(dāng)考慮到目前互聯(lián)網(wǎng)已經(jīng)存在這樣的請(qǐng)求注盈,他們雖然看起來(lái)可能不安全,但為了向下兼容叙赚,我們不能強(qiáng)制對(duì)這些請(qǐng)求做preflight request老客。既然不能強(qiáng)制做preflight request驗(yàn)證,那發(fā)這個(gè)東西就沒(méi)有什么意義了震叮。當(dāng)然沿量,我認(rèn)為在時(shí)機(jī)成熟的時(shí)候,我們可以引入一種強(qiáng)制CORS的機(jī)制冤荆,就像現(xiàn)在的強(qiáng)制HTTPS機(jī)制一樣朴则。我們可以約定瀏覽器預(yù)先發(fā)一個(gè)請(qǐng)求到目標(biāo)域名確定目標(biāo)域的服務(wù)器是否支持強(qiáng)制CORS。如果目標(biāo)域支持強(qiáng)制CORS,則瀏覽器對(duì)引用目標(biāo)域的任何資源請(qǐng)求都發(fā)出Origin頭乌妒,任何數(shù)據(jù)的發(fā)送都先發(fā)送preflight request陨簇。至于你的迷惑赋除,簡(jiǎn)單來(lái)說(shuō):CORS是允許受限的跨域訪問(wèn)侈玄,不是限制現(xiàn)有的跨域訪問(wèn)宜肉。沒(méi)有CORS之前我們不是不可以跨域訪問(wèn),而是要很彎彎繞(譬如說(shuō)JSONP和萬(wàn)能的服務(wù)器代發(fā))侦啸,而CORS則是提出一個(gè)方案可以讓我們直接了當(dāng)?shù)拿枋隹缬蛟L問(wèn)的需求并且加以控制槽唾。
*補(bǔ)充3
失禮:
簡(jiǎn)單請(qǐng)求的情況不需求發(fā)起preflight,也就不用先發(fā)起options請(qǐng)求然后再發(fā)起真實(shí)的請(qǐng)求光涂,也就是說(shuō)如果剛好允許跨域或者壓根沒(méi)跨域庞萍,它消耗資源少。簡(jiǎn)單請(qǐng)求的那些情況就是我們常用的操作忘闻,如果也需要preflight钝计,就浪費(fèi)資源了。因?yàn)閷?duì)我們自己而言的大量非跨域請(qǐng)求就不公平了齐佳。而對(duì)于非簡(jiǎn)單請(qǐng)求而言私恬,瀏覽器實(shí)行跨域預(yù)檢機(jī)制可以節(jié)約資源,也做了一道防線炼吴。因?yàn)槿绻蠖瞬辉试S跨域本鸣,就不需要發(fā)送正式的請(qǐng)求啦。退一步講硅蹦,正常后端都是會(huì)對(duì)跨域請(qǐng)求做過(guò)濾限制的荣德。不管你是簡(jiǎn)單還是復(fù)雜請(qǐng)求。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末提针,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子曹傀,更是在濱河造成了極大的恐慌辐脖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皆愉,死亡現(xiàn)場(chǎng)離奇詭異嗜价,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)幕庐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)久锥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人异剥,你說(shuō)我怎么就攤上這事瑟由。” “怎么了冤寿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵歹苦,是天一觀的道長(zhǎng)青伤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)殴瘦,這世上最難降的妖魔是什么狠角? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蚪腋,結(jié)果婚禮上丰歌,老公的妹妹穿的比我還像新娘。我一直安慰自己屉凯,他們只是感情好立帖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著神得,像睡著了一般厘惦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哩簿,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天宵蕉,我揣著相機(jī)與錄音,去河邊找鬼节榜。 笑死羡玛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宗苍。 我是一名探鬼主播稼稿,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼讳窟!你這毒婦竟也來(lái)了让歼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤丽啡,失蹤者是張志新(化名)和其女友劉穎谋右,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體补箍,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡改执,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坑雅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辈挂。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裹粤,靈堂內(nèi)的尸體忽然破棺而出终蒂,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布后豫,位于F島的核電站悉尾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挫酿。R本人自食惡果不足惜构眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望早龟。 院中可真熱鬧惫霸,春花似錦、人聲如沸葱弟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芝加。三九已至硅卢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間藏杖,已是汗流浹背将塑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝌麸,地道東北人点寥。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像来吩,于是被迫代替她去往敵國(guó)和親敢辩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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

  • ??2005 年,Jesse James Garrett 發(fā)表了一篇在線文章怠苔,題為“Ajax: A new App...
    霜天曉閱讀 885評(píng)論 0 1
  • 前沿: 最近總聽(tīng)到同事聊跨域得問(wèn)題同廉,于是自己抽空仔細(xì)的查閱了一下關(guān)于跨域的知識(shí)。說(shuō)到跨域嘀略,就得提到同源恤溶,跨域是指一...
    戈弋圖閱讀 1,805評(píng)論 0 4
  • 前言:對(duì)于跨域請(qǐng)求乓诽,很早之前就有去了解過(guò)帜羊,但因?yàn)橐恢标P(guān)注的都是服務(wù)器后端開(kāi)發(fā),故也就僅僅停留在概念的理解上而沒(méi)有機(jī)...
    ken_ljq閱讀 89,735評(píng)論 6 128
  • 題目1.什么是同源策略? 同源策略(Same origin Policy): 瀏覽器出于安全方面的考慮鸠天,只允許與本...
    FLYSASA閱讀 1,714評(píng)論 0 6
  • 引用自HTTP訪問(wèn)控制(CORS) 當(dāng) Web 資源請(qǐng)求由其它域名或端口提供的資源時(shí)讼育,會(huì)發(fā)起跨域 HTTP 請(qǐng)求(...
    有涯逐無(wú)涯閱讀 2,579評(píng)論 0 4