詳解ASP.NET中Identity的身份驗(yàn)證代碼
本篇內(nèi)容主要講述了實(shí)現(xiàn)基于微軟賬戶(hù)的第三方身份驗(yàn)證、實(shí)現(xiàn)雙因子身份驗(yàn)證、 驗(yàn)證碼機(jī)制這3個(gè)內(nèi)容。
實(shí)現(xiàn)基于微軟賬戶(hù)的第三方身份驗(yàn)證
在微軟提供的ASP.NET MVC模板代碼中,默認(rèn)添加了微軟、Google、twitter以及Facebook的賬戶(hù)登錄代碼(雖然被注釋了),另外針對(duì)國(guó)內(nèi)的一些社交賬戶(hù)提供了相應(yīng)的組件,所有組件都可以通過(guò)Nuget包管理器安裝:

從上圖中看到有優(yōu)酷、微信、QQ、微博等組件,其中一些是微軟提供的,一些是其它開(kāi)發(fā)者提供的。而本文將使用微軟賬戶(hù)為例來(lái)介紹如何實(shí)現(xiàn)一個(gè)第三方登錄。
注:本章主要代碼參考ASP.NET MVC模板代碼,所以在文章中只列出關(guān)鍵代碼,其余代碼與模板中的完全一致。
組件安裝及Key申請(qǐng)
在開(kāi)發(fā)之前首先需要通過(guò)Nuget安裝Microsoft.Owin.Security.MicrosoftAccount:

另外就是需要去微軟的開(kāi)發(fā)者中心使用微軟賬戶(hù)創(chuàng)建一個(gè)自己的應(yīng)用信息https://apps.dev.microsoft.com/,并保存應(yīng)用的ID以及密鑰用于對(duì)身份驗(yàn)證中間件進(jìn)行配置:

創(chuàng)建過(guò)程如下:
1. 點(diǎn)擊添加應(yīng)用按鈕,進(jìn)入應(yīng)用程序注冊(cè)頁(yè)面,填寫(xiě)應(yīng)用名稱(chēng)并點(diǎn)擊Create按鈕(注:由于我已經(jīng)有一個(gè)名稱(chēng)為My Blog的App,所以下面的Test App的創(chuàng)建流程僅僅是用于演示,后續(xù)的身份驗(yàn)證實(shí)際上是使用之前創(chuàng)建的My Blog):

2. 在后續(xù)頁(yè)面中點(diǎn)擊生成新密碼來(lái)生成密鑰(注:該密碼只顯示一次,需要在彈出框中復(fù)制并保存下來(lái)):

3. 添加平臺(tái):點(diǎn)擊添加平臺(tái)按鈕,添加一個(gè)Web平臺(tái),并在平臺(tái)的重定向Url中填入本地調(diào)試的地址信息(注:一定需要啟動(dòng)HTTPS并且地址后需要添加signin-microsoft,VS可以在項(xiàng)目的屬性中開(kāi)啟SSL,并設(shè)置SSL的URL):


4. 保存更改。
添加中間件
在上一篇文章中介紹了,第三方賬戶(hù)身份驗(yàn)證除了特定賬戶(hù)身份驗(yàn)證中間件外,還需要添加一個(gè)消極模式的外部Cookie身份驗(yàn)證中間件,所以首先需要在項(xiàng)目的Startup文件中添加一下代碼:

然后再在該中間件后加入微軟身份驗(yàn)證中間件(注:中間件順序會(huì)影響處理流程,微軟身份驗(yàn)證中間件必須在外部Cookie中間件后),并設(shè)置上面創(chuàng)建的應(yīng)用ID及密鑰:
添加Controller及頁(yè)面的功能支持
現(xiàn)在可以說(shuō)應(yīng)用中已經(jīng)支持微軟的賬戶(hù)身份驗(yàn)證了,但是在應(yīng)用中還未提供微軟身份驗(yàn)證的入口,以及登陸后用戶(hù)信息的補(bǔ)全等功能。
1. 在頁(yè)面上添加驗(yàn)證入口,在Login頁(yè)面上加入以下代碼,通過(guò)AuthenticationManager來(lái)獲取所有的第三方身份驗(yàn)證方式,并生成對(duì)應(yīng)鏈接:


2. 在AccountController中添加ExternalLogin Action方法(注:該方法主要目的是調(diào)用AuthenticationManager的Challenge方法來(lái)觸發(fā)微軟身份驗(yàn)證中間件的ResponseChallenge方法來(lái)完成頁(yè)面的跳轉(zhuǎn)):

其中ChallengeResult是一個(gè)自定義的ASP.NET MVC Reuslt類(lèi)型:

3. 加入第三方驗(yàn)證后的回調(diào)方法ExternalLoginCallback,該回調(diào)方法是獲取第三方身份驗(yàn)證后的用戶(hù)信息,然后在本地?cái)?shù)據(jù)庫(kù)中查找該用戶(hù),如果存在那么登錄成功,否則需要對(duì)該用戶(hù)信息進(jìn)行補(bǔ)全。

4. 添加第三方賬戶(hù)信息補(bǔ)全頁(yè)面及Action方法,其中action方法接收到補(bǔ)全的用戶(hù)信息后完成用戶(hù)注冊(cè)功能,但要注意的是第三方賬戶(hù)沒(méi)有密碼,僅僅是在AspNetUserLogins表中添加了第三方驗(yàn)證的信息:
運(yùn)行結(jié)果:
1.訪問(wèn)登錄頁(yè)面出現(xiàn)Microsoft的按鈕(注:必須使用HTTPS地址才能正常的使用微軟身份驗(yàn)證):

2. 點(diǎn)擊微軟身份驗(yàn)證按鈕后,跳轉(zhuǎn)到微軟賬戶(hù)登錄頁(yè)面:


3. 完成登錄后,由于是第一次登錄,所以會(huì)跳轉(zhuǎn)到信息補(bǔ)全頁(yè)面:

輸入郵箱后將登錄成功:

數(shù)據(jù)庫(kù)中的信息:

上圖中可以看到無(wú)密碼,然后在Login表中有一條數(shù)據(jù):

實(shí)現(xiàn)雙因子身份驗(yàn)證
Identity的雙因子身份驗(yàn)證實(shí)際上是Identity的一個(gè)內(nèi)置功能,為什么說(shuō)是內(nèi)置呢?因?yàn)橹恍枰獙?shí)現(xiàn)信息的發(fā)送(如郵件、短信等),然后再對(duì)Identity中的SignInManager進(jìn)行簡(jiǎn)單的配置然后添加一些用于發(fā)送、填寫(xiě)驗(yàn)證碼的頁(yè)面就可以完成。所以首先需要完成的就是實(shí)現(xiàn)信息發(fā)送功能。
注:這里信息發(fā)送功能使用將信息寫(xiě)到硬盤(pán)的方式模擬。
1. 實(shí)現(xiàn)信息的發(fā)送:
在ASP.NET MVC默認(rèn)的模板中就為我們創(chuàng)建了如下代碼:

默認(rèn)的郵件及短信發(fā)送器,只不過(guò)它沒(méi)有實(shí)現(xiàn),僅僅是返回了一個(gè)空值,現(xiàn)在使用寫(xiě)硬盤(pán)的方式將信息寫(xiě)到硬盤(pán)上:

2. 完成UserManager的雙因子驗(yàn)證配置:

三個(gè)關(guān)鍵點(diǎn):1. TokenProvider,它用來(lái)生成驗(yàn)證碼。2. 信息格式。3. 信息發(fā)送服務(wù)。
3. 在身份驗(yàn)證管道中加入雙因子驗(yàn)證中間件:

兩個(gè)中間件前者用于處理二次驗(yàn)證,后者用于記住登錄狀態(tài),下次訪問(wèn)系統(tǒng)時(shí)自動(dòng)登錄。
4. 添加驗(yàn)證碼發(fā)送方式選擇以及驗(yàn)證碼填寫(xiě)頁(yè)面及相應(yīng)的Action方法(代碼略)。
5. 在數(shù)據(jù)庫(kù)中將演示用的用戶(hù)信息改為啟用二次驗(yàn)證(注:模板代碼中有用于管理個(gè)人信息的功能,此處省略了實(shí)現(xiàn),直接通過(guò)修改數(shù)據(jù)數(shù)據(jù)的方式開(kāi)啟用戶(hù)的雙因子驗(yàn)證、添加電話(huà)號(hào)碼等):

6. 運(yùn)行結(jié)果:
登錄后需要選擇驗(yàn)證碼發(fā)送方式:

選擇后點(diǎn)擊提交按鈕,頁(yè)面調(diào)整到驗(yàn)證頁(yè)面的同時(shí),指定的文件中生成了需要的驗(yàn)證碼:


填寫(xiě)驗(yàn)證碼后點(diǎn)擊提交按鈕,則登錄成功:


注:雙因子驗(yàn)證也可以應(yīng)用到第三方賬戶(hù)的登錄方式上,雙因子驗(yàn)證只與用戶(hù)有關(guān)與身份驗(yàn)證方式無(wú)關(guān)。
驗(yàn)證碼機(jī)制
對(duì)于雙因子驗(yàn)證來(lái)說(shuō),它實(shí)際上就是在普通驗(yàn)證或第三方賬戶(hù)驗(yàn)證的基礎(chǔ)上增加了驗(yàn)證碼的發(fā)送和驗(yàn)證兩個(gè)環(huán)節(jié),那么對(duì)于驗(yàn)證碼這個(gè)主體Identity是如何來(lái)維護(hù)的呢?
在上面的介紹中,有一個(gè)環(huán)節(jié)就是需要通過(guò)對(duì)UserManager進(jìn)行配置以支持雙因子驗(yàn)證的消息發(fā)送、消息生成等等:

根據(jù)這個(gè)代碼看來(lái)XXXTokenProvider是專(zhuān)門(mén)用來(lái)維護(hù)驗(yàn)證碼的,而XXXService是用來(lái)發(fā)送的,所以這里將對(duì)TokenProvider進(jìn)行說(shuō)明,了解驗(yàn)證碼是如何維護(hù)的:

上圖是TokenProvider相關(guān)的一個(gè)簡(jiǎn)單類(lèi)圖,從類(lèi)圖中可以看出TokenProvider實(shí)際上是實(shí)現(xiàn)了一個(gè)名為IUserTokenProvider的接口,該接口中有4個(gè)方法,它們的作用分別是:
● GenerateAsync:根據(jù)UserManager以及User信息來(lái)生成一個(gè)令牌(Token)。
● IsValidProviderForUserAsync:判斷這個(gè)Token提供器對(duì)這個(gè)用戶(hù)是否是有效的(如果使用短信驗(yàn)證,但是該用戶(hù)沒(méi)有設(shè)置手機(jī)號(hào),那么就是無(wú)效的)。
● NotifyAsync:當(dāng)Token生成后調(diào)用該方法通知用戶(hù),如短信或郵件通知。
● ValidateAsync:用于驗(yàn)證Token是否有效。
而TotpSecutityStampBasedTokenProvider是一個(gè)實(shí)現(xiàn)了IUserTokenProvider接口的,通過(guò)用戶(hù)安全戳生成驗(yàn)證碼的生成器:

從代碼中可以看到該算法是基于rfc6238(TOTP: Time-Based One-Time Password Algorithm,基于時(shí)間的一次性密碼算法) https://tools.ietf.org/html/rfc6238,然后通過(guò)用戶(hù)的安全戳以及GetUserModifierAsync方法生成特定的信息熵來(lái)完成密碼加密,關(guān)于信息熵可參考:https://www.zhihu.com/question/22178202,上面將生成后的令牌執(zhí)行ToString("D6")是將其轉(zhuǎn)換為一個(gè)6位數(shù)字的字符串。
而Token的驗(yàn)證方式和生成差不多都是通過(guò)用戶(hù)安全戳和信息熵來(lái)驗(yàn)證提交的驗(yàn)證碼(它實(shí)際上是一種hash算法):

以上已經(jīng)解釋了最初驗(yàn)證碼的生成和驗(yàn)證的問(wèn)題,所以對(duì)于EmailTokenProvider和PhoneNumberTokenProvider只是對(duì)熵的生成、對(duì)Provider的有效性(是否存在Email或電話(huà)號(hào)碼)、通知方式進(jìn)行了修改,下面是PhoneNumberTokenProvider相關(guān)代碼:



小結(jié)
本章主要是使用代碼的形式實(shí)現(xiàn)了ASP.NET中的第三方驗(yàn)證和雙因子驗(yàn)證,文中的代碼都來(lái)自ASP.NET MVC的模板,所以文中僅僅是對(duì)關(guān)鍵的代碼進(jìn)行了介紹,一些細(xì)節(jié)的內(nèi)容可參考完整代碼。其中第三方驗(yàn)證使用的是微軟賬戶(hù),如果有環(huán)境支持可以嘗試國(guó)內(nèi)的微信、QQ等身份驗(yàn)證。
另外在文章最后對(duì)驗(yàn)證碼的生成和校驗(yàn)代碼進(jìn)行了分析,知道了它是基于Hash算法的信息加密、驗(yàn)證的機(jī)制來(lái)實(shí)現(xiàn)的。
ASP.NET MVC基于Identity提供了非常完善、強(qiáng)大的用戶(hù)管理和身份驗(yàn)證功能,除了以上介紹的以外還有賬戶(hù)鎖定、注冊(cè)郵箱或短信驗(yàn)證功能,基本上已經(jīng)涵蓋了現(xiàn)在開(kāi)發(fā)常用的功能,但這些功能被一個(gè)模板實(shí)現(xiàn)了,所以ASP.NET強(qiáng)大嗎?
- IdentityServer4 QuckStart 授權(quán)與自定義Claims的問(wèn)題
- IdentityServer4實(shí)現(xiàn).Net Core API接口權(quán)限認(rèn)證(快速入門(mén))
- JPA中EntityListeners注解的使用詳解
- php中html_entity_decode實(shí)現(xiàn)HTML實(shí)體轉(zhuǎn)義
- EF(EntityFramework) 插入或更新數(shù)據(jù)報(bào)錯(cuò)的解決方法
- c# 使用Entity Framework操作Access數(shù)據(jù)庫(kù)的示例
- Idea自動(dòng)生成Entity實(shí)現(xiàn)過(guò)程詳解
相關(guān)文章
詳解C#中的System.Timers.Timer定時(shí)器的使用和定時(shí)自動(dòng)清理內(nèi)存應(yīng)用
這篇文章主要介紹了詳解C#中的System.Timers.Timer定時(shí)器的使用和定時(shí)自動(dòng)清理內(nèi)存應(yīng)用,需要的朋友可以參考下2017-06-06
C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐
有時(shí)我們可能會(huì)遇到需要的資料或教程被分成了幾部分存放在多個(gè)PDF文件中,本文主要介紹了C#實(shí)現(xiàn)PDF合并的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
WPF利用TabControl控件實(shí)現(xiàn)拖拽排序功能
在UI交互中,拖拽操作是一種非常簡(jiǎn)單友好的交互,這篇文章主要為大家介紹了WPF如何利用TabControl控件實(shí)現(xiàn)拖拽排序功能,需要的小伙伴可以參考一下2023-10-10
C#實(shí)現(xiàn)基于XML配置MenuStrip菜單的方法
這篇文章主要介紹了C#實(shí)現(xiàn)基于XML配置MenuStrip菜單的方法,涉及C#使用XML配置MenuStrip菜單的原理與實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08
C# 延遲Task.Delay()和Thread.Sleep()的具體使用
Thread.Sleep()是同步延遲,Task.Delay()是異步延遲,本文主要介紹了C# 延遲Task.Delay()和Thread.Sleep()的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-01-01
關(guān)于Unity C# Mathf.Abs()取絕對(duì)值性能測(cè)試詳解
這篇文章主要給大家介紹了關(guān)于Unity C# Mathf.Abs()取絕對(duì)值性能測(cè)試的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Unity C#具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
C#實(shí)現(xiàn)帶引導(dǎo)窗體的窗體設(shè)計(jì)操作流程
很多時(shí)候,我們的窗體設(shè)計(jì)需要一個(gè)引導(dǎo)窗體,當(dāng)打開(kāi)一個(gè)項(xiàng)目的窗體時(shí),默認(rèn)的是先打開(kāi)一個(gè)歡迎或介紹項(xiàng)目信息的引導(dǎo)窗體,幾秒鐘后再打開(kāi)項(xiàng)目的主窗體,本文給大家介紹了C#實(shí)現(xiàn)帶引導(dǎo)窗體的窗體設(shè)計(jì)操作流程,感興趣的朋友可以參考下2024-04-04

