登录工程:传统 Web 应用中的身份验证技术
標(biāo)題中 “傳統(tǒng) Web 應(yīng)用” 這一說法也并沒有什么官方定義,只是為了與“現(xiàn)代化 Web 應(yīng)用”形成比較而自擬的一個(gè)概念。所謂現(xiàn)代化 Web 應(yīng)用指的是那些基于分布式架構(gòu)思想設(shè)計(jì)的,面向多個(gè)端提供穩(wěn)定可靠的高可用服務(wù),并且在需要時(shí)能夠橫向擴(kuò)展的 Web 應(yīng)用。相對而言,傳統(tǒng) Web 應(yīng)用則主要是直接面向 PC 用戶的?Web 應(yīng)用程序,采用單體架構(gòu)較多,也可能在內(nèi)部采用 SOA 的分布式運(yùn)算技術(shù)。
一直以來,傳統(tǒng) Web 應(yīng)用為構(gòu)成互聯(lián)網(wǎng)發(fā)揮了重要作用。因此傳統(tǒng) Web 應(yīng)用中的身份驗(yàn)證技術(shù)經(jīng)過了幾代的發(fā)展,已經(jīng)解決了不少實(shí)際問題,并最終沉淀了一些實(shí)踐模式。
安全議題不容忽視
在講述多種身份鑒權(quán)技術(shù)之前,要強(qiáng)調(diào)一點(diǎn):在構(gòu)建互聯(lián)網(wǎng) Web 應(yīng)用過程中,無論使用哪種技術(shù),在傳輸用戶名和密碼時(shí),請一定要采用安全連接。因?yàn)闊o論采用何種鑒權(quán)模型,都無法保護(hù)用戶憑據(jù)在傳輸過程中不被竊取。
Basic 和 Digest 鑒權(quán)
基于 HTTP 的 Web 應(yīng)用離不開 HTTP 本身的安全特性中關(guān)于身份鑒權(quán)的部分。雖然 HTTP 標(biāo)準(zhǔn)定義了好幾種鑒權(quán)方式,但真正供 Web 應(yīng)用開發(fā)者選擇的并不多,這里簡要回顧一下曾經(jīng)被廣泛運(yùn)用過的 Basic 和 Digest 鑒權(quán)。
不知道讀者是否熟悉一種最直接向服務(wù)器提供身份的方式,即在 URL 中直接寫上用戶名和密碼:
http://user:passwd@www.server.com/index.html
這就是 Basic 鑒權(quán)的一種形式。
Basic 和 Digest 是通過在 HTTP 請求頭中直接包含用戶名和密碼,或者它們的哈希值來向服務(wù)器傳輸用戶憑據(jù)的方法。Basic 鑒權(quán)直接在每個(gè)請求請求的頭部或 URL 中包含明文的用戶名或密碼,或者經(jīng)過 Base64 編碼過的用戶名或密碼;而 Digest 則會(huì)使用服務(wù)器返回的隨機(jī)值,對用戶名和密碼拼裝后,使用多次 MD5 哈希處理后再向服務(wù)器傳輸。服務(wù)器在處理每個(gè)請求之前,讀取收到的憑據(jù),并鑒定用戶的身份。
Basic 和 Digest 鑒權(quán)有一系列的缺陷。它們需要在每個(gè)請求中提供憑據(jù),因此提供“記住登錄狀態(tài)”功能的網(wǎng)站中,不得不將用戶憑據(jù)緩存在瀏覽器中,增加了用戶的安全風(fēng)險(xiǎn)。Basic 鑒權(quán)基本不對用戶名和密碼等敏感信息進(jìn)行預(yù)處理,所以只適合于較安全的安全環(huán)境,如通過 HTTPS 安全連接傳輸,或者局域網(wǎng)。看起來更安全的 Digest 在非安全連接傳輸過程中,也無法抵御中間人通過篡改響應(yīng)來要求客戶端降級(jí)為 Basic 鑒權(quán)的攻擊。Digest 鑒權(quán)還有一個(gè)缺陷:由于在服務(wù)器端需要核對收到的、由客戶端經(jīng)過多次 MD5 哈希值的合法性,需要使用原始密碼做相同的運(yùn)算,這讓服務(wù)器無法在存儲(chǔ)密碼之前對其進(jìn)行不可逆的加密。Basic 和 Digest 鑒權(quán)的缺陷決定了它們不可能在互聯(lián)網(wǎng) Web 應(yīng)用中被大量采用。
簡單實(shí)用的登錄技術(shù)
對于互聯(lián)網(wǎng) Web 應(yīng)用來說,不采用 Basic 或 Digest 鑒權(quán)的理由主要有兩個(gè):
1. 不能接受在每個(gè)請求中發(fā)送用戶名和密碼憑據(jù)
2. 需要在服務(wù)器端對密碼進(jìn)行不可逆的加密
因此,互聯(lián)網(wǎng) Web 應(yīng)用開發(fā)已經(jīng)形成了一個(gè)基本的實(shí)踐模式,能夠在服務(wù)端對密碼強(qiáng)加密之后存儲(chǔ),并且盡量減少鑒權(quán)過程中對憑據(jù)的傳輸。其過程如下圖所示:
基于 Cookie 和 Session ?的鑒權(quán)過程
這一過程的原理很簡單,專門發(fā)送一個(gè)鑒權(quán)請求,只在這個(gè)請求中包含原始用戶名和密碼憑據(jù),經(jīng)服務(wù)器驗(yàn)證合法之后,由服務(wù)器發(fā)給一個(gè)會(huì)話標(biāo)識(shí)(Session ID),客戶端將會(huì)話標(biāo)識(shí)存儲(chǔ)在 Cookie 中,服務(wù)器記錄會(huì)話標(biāo)識(shí)與經(jīng)過驗(yàn)證的用戶的對應(yīng)關(guān)系;后續(xù)客戶端使用會(huì)話標(biāo)識(shí)、而不是原始憑據(jù)去與服務(wù)器交互,服務(wù)器讀取到會(huì)話標(biāo)識(shí)后從自身的會(huì)話存儲(chǔ)中讀取已在第一個(gè)鑒權(quán)請求中驗(yàn)證過的用戶身份。為了保護(hù)用戶的原始憑據(jù)在傳輸中的安全,只需要為第一個(gè)鑒權(quán)請求構(gòu)建安全連接支持。
服務(wù)端的代碼包含首次鑒權(quán)和后續(xù)檢查并授權(quán)訪問的過程:
IUser user;
if( validateLogin( nameFromReq, pwdFromReq, out user?)){
? ? Session["CurrentUser"] = user;
}
(首次鑒權(quán))
IUser user = Session["CurrentUser"] as IUser;
if(?user == null ){
? ? Response.Redirect( "/login?return_uri=" + Request.Url.ToString() );
? ? return;
}
(后續(xù)檢查并拒絕未識(shí)別的用戶)
類似這樣的技術(shù)簡易方便,容易操作,因此大量被運(yùn)用于很多互聯(lián)網(wǎng) Web 應(yīng)用中。它在客戶端和傳輸憑據(jù)過程中幾乎沒有做特殊處理,所以在這兩個(gè)環(huán)節(jié)尤其要注意對用戶憑據(jù)的保護(hù)。不過,隨著我們對系統(tǒng)的要求越來越復(fù)雜,這樣簡易的實(shí)現(xiàn)方式也有一些明顯的不足。比如,如果不加以封裝,很容易出現(xiàn)在服務(wù)器應(yīng)用程序代碼中出現(xiàn)大量對用戶身份的重復(fù)檢查、錯(cuò)誤的重定向等;不過最明顯的問題可能是對服務(wù)器會(huì)話存儲(chǔ)的依賴,服務(wù)器程序的會(huì)話存儲(chǔ)往往在服務(wù)器程序重啟之后丟失,因此可能會(huì)導(dǎo)致用戶突然被登出的情況。雖然可以引入單獨(dú)的會(huì)話存儲(chǔ)程序來避免這類問題,但引入一個(gè)新的中間件就會(huì)增加系統(tǒng)的復(fù)雜性。
傳統(tǒng) Web 應(yīng)用中身份驗(yàn)證最佳實(shí)踐
上文提到的簡單實(shí)用的登錄技術(shù)已經(jīng)可以幫助建立對用戶身份驗(yàn)證的基本圖景,在一些簡單的應(yīng)用場景中已經(jīng)足夠滿足需求了。然而,用戶鑒權(quán)就是那種“你可以有很多種方法,就是不怎么優(yōu)雅” 的問題。
最佳實(shí)踐指的是那些經(jīng)過了大量驗(yàn)證,被證明有用的方法。而用戶鑒權(quán)的最佳實(shí)踐就是使用自包含的、含有加密內(nèi)容的 Cookie 作為替代憑據(jù)。其鑒權(quán)過程與上文所提到基于會(huì)話標(biāo)識(shí)的技術(shù)沒有什么區(qū)別,而主要區(qū)別在于不再頒發(fā)會(huì)話標(biāo)識(shí),取而代之的是一個(gè)代表身份的、經(jīng)過加密的?“身份 Cookie”。
1. 只在鑒權(quán)請求中發(fā)送一次用戶名和密碼憑據(jù)
2. 成功憑據(jù)之后,由服務(wù)器生成代表用戶身份的?Cookie,發(fā)送給客戶端
3. 客戶端在后續(xù)請求中攜帶上一步中收到的 “身份 Cookie”
4. 服務(wù)器解密"身份 Cookie",并對需要訪問的資源予以授權(quán)
這樣,我們消除了對服務(wù)器會(huì)話存儲(chǔ)的依賴,Cookie 本身就有有效期的概念,因此順便能夠輕松提供“記住登錄狀態(tài)”的功能。
另外,由于解密 Cookie 既而檢查用戶身份的操作相對繁瑣,迫使工程師不得不考慮對其抽取專門的服務(wù),最終采用了面向切面的模式對身份驗(yàn)證的過程進(jìn)行了封裝,而開發(fā)時(shí)只需要使用一些特性標(biāo)注(Attribute Annotation)對特定資源予以標(biāo)記即可輕松完成身份驗(yàn)證預(yù)處理。
傳統(tǒng) Web 應(yīng)用中的單點(diǎn)登錄
單點(diǎn)登錄的需求在向用戶提供多種服務(wù)的企業(yè)普遍存在,出發(fā)點(diǎn)是希望用戶在一個(gè)站點(diǎn)中登錄之后,在其他兄弟站點(diǎn)中就不需要再次登錄。
如果多個(gè)子站所在頂級(jí)域名一致,基于上文所述的實(shí)踐,可以基于 Cookie 共享實(shí)現(xiàn)最簡單的單點(diǎn)登錄:在多個(gè)子站中使用相同的加密、解密配置,并且在用戶登錄成功后設(shè)置身份 Cookie 時(shí)將 domain 值設(shè)置為頂級(jí)域名即可。這樣,只要在其中一個(gè)網(wǎng)站登錄,其身份 Cookie 將在用戶訪問其他子站時(shí)也一起帶上。不過實(shí)際情況中,這個(gè)方案的應(yīng)用場景很有限,畢竟各個(gè)子站使用的用戶數(shù)據(jù)模型可能不完全一致,而加密密鑰多處共享也增加了服務(wù)器應(yīng)用程序的安全風(fēng)險(xiǎn)。
對于單點(diǎn)登錄需求來說,域名相同與否并不是最大的挑戰(zhàn),集成登錄系統(tǒng)對各個(gè)子站點(diǎn)的系統(tǒng)在設(shè)計(jì)上的影響才是。我們希望便利用戶的同時(shí),也期待各個(gè)子系統(tǒng)仍擁有獨(dú)立用戶身份、獨(dú)立管理和運(yùn)維的靈活性。因此我們引入獨(dú)立的鑒權(quán)子站點(diǎn)。當(dāng)用戶到達(dá)業(yè)務(wù)站點(diǎn)?A 時(shí),被重定向到鑒權(quán)站點(diǎn);登錄成功之后,用戶被重定向回到業(yè)務(wù)站點(diǎn) A、同時(shí)附加一個(gè)指示“已有用戶登錄”的令牌串——此時(shí)業(yè)務(wù)站點(diǎn) A 使用令牌串,在服務(wù)器端從鑒權(quán)子站點(diǎn)查詢并記錄當(dāng)前已登錄的用戶。當(dāng)用戶到達(dá)業(yè)務(wù)站點(diǎn)?B 時(shí),執(zhí)行相同流程。由于已有用戶登錄,所以用戶登錄的過程會(huì)被自動(dòng)省略。
這樣的單點(diǎn)登錄系統(tǒng)能夠較好地解決在多個(gè)站點(diǎn)中共享用戶登錄狀態(tài)的需求。不過,如果在編程實(shí)踐過程中略有差池,就會(huì)讓用戶陷入巨大的安全風(fēng)險(xiǎn)中。例如,在上述重定向過程中,一旦鑒權(quán)系統(tǒng)未能驗(yàn)證返回 URL 的合法性,就容易導(dǎo)致用戶被釣魚網(wǎng)站利用。在傳統(tǒng) Web 應(yīng)用開發(fā)實(shí)踐中,被廣泛部署的身份驗(yàn)證體系是比較重量級(jí)的?WS-Federation 和相對輕量級(jí)的?OpenID 等技術(shù)。
總結(jié)
本文簡要總結(jié)了傳統(tǒng) Web 應(yīng)用中被廣泛使用的幾種典型的用戶登錄時(shí)的鑒權(quán)處理流程。總體來說,在單體 Web 應(yīng)用中,身份驗(yàn)證過程并不復(fù)雜,只要稍加管理,可以較輕松地解決用戶鑒權(quán)的問題。但在傳統(tǒng) Web 應(yīng)用中,為了解決單點(diǎn)登錄的需求,人們也嘗試了多種方式,最終仍然只有使用一些較復(fù)雜的方案才能較好地解決問題。
在現(xiàn)代化 Web 應(yīng)用中,圍繞登錄這一需求,儼然已經(jīng)衍生出了一個(gè)新的工程。“登錄工程” 并不簡單,在后續(xù)篇目中將會(huì)介紹現(xiàn)代化 Web 應(yīng)用的典型需求及解決方法。
原文地址:http://www.jianshu.com/p/6ca51a8d66bd
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的登录工程:传统 Web 应用中的身份验证技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JWT【JSON Web Token】
- 下一篇: TagHelper是怎么实现的