問(wèn)題很簡(jiǎn)單,開發(fā)一個(gè)后臺(tái)服務(wù)。這個(gè)服務(wù)呢,包含了幾個(gè)團(tuán)隊(duì)的后臺(tái)服務(wù)之間的相互調(diào)用。這些后臺(tái)服務(wù)之間的頂級(jí)域名(相關(guān)概念后面會(huì)解釋)相同,請(qǐng)求卻報(bào)錯(cuò)說(shuō)有跨域問(wèn)題。經(jīng)調(diào)查,原因是調(diào)用方是http服務(wù),被調(diào)用方是https服務(wù),引起的跨域問(wèn)題。如下圖所示: 這里假設(shè)問(wèn)題到生產(chǎn)環(huán)境才發(fā)現(xiàn),本篇使用COE的寫法來(lái)論述問(wèn)題。COE(correction of error)更正錯(cuò)誤,是很多公司用來(lái)做問(wèn)題或者事故復(fù)盤的手段。COE因?yàn)槊Q中帶著“有人犯錯(cuò)啦”的意思,很多公司為了表明“追究責(zé)任不是目的,重要的是吸取教訓(xùn),避免同樣和同類的問(wèn)題”,把COE叫做casestudy個(gè)案研究。實(shí)踐證明,不管叫什么,恐懼一點(diǎn)也不會(huì)減少。 事故描述 red服務(wù)從前端調(diào)用gray服務(wù)時(shí)報(bào)錯(cuò):跨域錯(cuò)誤。經(jīng)排查確認(rèn)由于http的域名下從前端調(diào)用https導(dǎo)致。 事故責(zé)任人 都是我的錯(cuò) 事故影響 新服務(wù)上線,無(wú)正式業(yè)務(wù)接入,無(wú)業(yè)務(wù)影響。 時(shí)間線 14:00 灰度發(fā)布新版本并進(jìn)行驗(yàn)證 14:20 驗(yàn)證發(fā)現(xiàn)使用Chrome瀏覽器通過(guò)【檢查】-【網(wǎng)絡(luò)】功能觀察到發(fā)起了一個(gè)OPTIONS類型的COR跨域請(qǐng)求,請(qǐng)求報(bào)錯(cuò): 15:00 確認(rèn)是http訪問(wèn)https導(dǎo)致違反了同源策略,導(dǎo)致跨域問(wèn)題。新服務(wù)上線,無(wú)正式業(yè)務(wù)接入,無(wú)業(yè)務(wù)影響。所以未進(jìn)行版本回滾。 根本原因分析 1、為什么會(huì)發(fā)生問(wèn)題? 出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源HTTP請(qǐng)求。當(dāng)一個(gè)資源從與該資源本身所在的服務(wù)器不同源,請(qǐng)求一個(gè)資源時(shí),資源會(huì)發(fā)起一個(gè)跨域 HTTP 請(qǐng)求。 同源的判斷標(biāo)準(zhǔn)是:
以上三者都要相同。發(fā)生問(wèn)題的請(qǐng)求,由于其中域名默認(rèn)只要求頂級(jí)域名相同,此條件滿足;協(xié)議一個(gè)是https,一個(gè)是http,不滿足;https端口為443,http端口為80,不滿足??傮w不滿足同源策略,而且被訪問(wèn)的后端沒(méi)有做跨域處理。 2、為什么沒(méi)有做跨域處理? 因?yàn)轫敿?jí)域名相同,并未考慮到有跨域問(wèn)題。且整個(gè)在測(cè)試環(huán)境下測(cè)試通過(guò),并未發(fā)生問(wèn)題。 3、為什么測(cè)試環(huán)境沒(méi)有問(wèn)題? 因?yàn)闇y(cè)試環(huán)境,同時(shí)支持http和https兩種方式,當(dāng)時(shí)配置時(shí)沒(méi)有考慮兩者直接的差異,直接使用http的路徑。 在生產(chǎn)環(huán)境,根據(jù)安全的要求,關(guān)閉了http方式,只能使用https方式,造成問(wèn)題。 4、為什么調(diào)用端使用http協(xié)議? 因?yàn)檎{(diào)用端的框架是幾年前的老系統(tǒng),線上環(huán)境追求穩(wěn)定性,變更成本高,目前還維持之前的現(xiàn)狀。 5、為什么調(diào)用方是post調(diào)用,實(shí)際上的請(qǐng)求卻是options? 出于安全考慮,并不是所有域名訪問(wèn)后端服務(wù)都可以。其實(shí)在正式跨域之前,瀏覽器會(huì)根據(jù)需要發(fā)起一次預(yù)檢(也就是option請(qǐng)求)。預(yù)檢請(qǐng)求不成功,不會(huì)發(fā)起正式請(qǐng)求。 瀏覽器將CORS請(qǐng)求分為兩類:簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-simple-request),簡(jiǎn)單請(qǐng)求瀏覽器不會(huì)預(yù)檢,而非簡(jiǎn)單請(qǐng)求會(huì)預(yù)檢。 同時(shí)滿足下列三大條件,就屬于簡(jiǎn)單請(qǐng)求,否則屬于非簡(jiǎn)單請(qǐng)求
對(duì)于簡(jiǎn)單請(qǐng)求,瀏覽器直接請(qǐng)求,會(huì)在請(qǐng)求頭信息中,增加一個(gè)origin字段,來(lái)說(shuō)明本次請(qǐng)求來(lái)自哪個(gè)源(協(xié)議+域名+端口)。服務(wù)器根據(jù)這個(gè)值,來(lái)決定是否同意該請(qǐng)求,服務(wù)器返回的響應(yīng)會(huì)多幾個(gè)頭信息字段,如圖所示:上面的頭信息中,三個(gè)與CORS請(qǐng)求相關(guān),都是以Access-Control-開頭。
經(jīng)驗(yàn)教訓(xùn) 首先是知識(shí)方面:之前對(duì)同域有誤解,認(rèn)為只需要頂級(jí)域名相同。實(shí)際上需要符合域名、協(xié)議和端口三者同源的同源策略。 其次是沒(méi)有對(duì)線上線下的差異做仔細(xì)的調(diào)研分析。 后續(xù)優(yōu)化 首先要修復(fù)問(wèn)題,跨域有風(fēng)險(xiǎn)。可以使用調(diào)用方自帶的代理功能。被調(diào)用方提供一個(gè)RPC調(diào)用,調(diào)用方使用代理來(lái)轉(zhuǎn)換。 小知識(shí) 根域名 簡(jiǎn)單的來(lái)說(shuō)就是類似.com這種,或者中國(guó)的.cn英國(guó)的.uk日本的.jp,這些是由“互聯(lián)網(wǎng)名稱與數(shù)字地址分配機(jī)構(gòu)”(The Internet Corporation for Assigned Names and Numbers ,簡(jiǎn)稱ICANN)來(lái)分配的。 頂級(jí)域名 簡(jiǎn)單來(lái)說(shuō)頂級(jí)域名就是就是在根域名的前面加上你自己定義的字母或數(shù)字等字符串包括連字符-(包括連字符但是不能連續(xù)是連字符,且連字符不能在第一個(gè))例如這樣web.com或者web.cn這樣的既是頂級(jí)域名。 二級(jí)域名 當(dāng)你注冊(cè)了一個(gè)頂級(jí)域名后,例如web.cn,你就可以在你的互聯(lián)網(wǎng)服務(wù)提供商的dns解析系統(tǒng)上自由分配你的二級(jí)域名或者三級(jí)域名等等,web1.web.cn,web2.web1.web.cn等等,以此類推根域名前面有幾個(gè)字符串就是幾級(jí)域名。 |
|