MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN
在Membership系列的最后一篇引入了ASP.NET Identity,看到大家對它還是挺感興趣的,于是來一篇詳解登錄原理的文章。本文會涉及到Claims-based(基于聲明)的認(rèn)證,我們會詳細(xì)介紹什么是Claims-based認(rèn)證,它與傳統(tǒng)認(rèn)證方式的區(qū)別,以及它的特點。同時我們還會介紹OWIN (Open Web Interface for .NET) 它主要定義了Web Server 和Web Application之間的一些行為,然后實現(xiàn)這兩個組件的解耦(當(dāng)然遠(yuǎn)不止這么點東西,我相信OWIN馬上就會掀起一場血雨腥風(fēng))ASP.NET Identity是如何利用OWin實現(xiàn)登錄的,都是干貨,同學(xué),你準(zhǔn)備好學(xué)習(xí)了么??
目錄
- ASP.NET Identity 登錄原理
- 什么是Claims-based (基于聲明) 的認(rèn)證
- ASP.NET 下Claims-based 認(rèn)證的實現(xiàn)
- 到底什么是OWIN
- 問題引入:為什么要解耦服務(wù)器與應(yīng)用程序
- OWin如何做到解耦?
- 微軟對OWin的開源實現(xiàn)Katana?
- OWin Authentication(認(rèn)證)
- Forms 認(rèn)證
- MVC 5 默認(rèn)的start up配置類
- 將Owin Middleware綁定到IIS 集成模式的管道
- CookieAuthenticationMiddelware 負(fù)責(zé)讀取用戶信息cookie
- CookieAuthenticationMiddelware 對cookie的加密方式
ASP.NET Identity登錄原理
廢話少說,我們直接切入正題。在上一篇從Membership到ASP.NET Identity,我們已經(jīng)給了一個簡單的實例,并且大致的描述了一下ASP.NET Identity的結(jié)構(gòu)體系,但是ASP.NET Identity主要提供的功能是幫助我們管理用戶,角色等信息,它主要負(fù)責(zé)的是存儲這一塊,也就是我們的信息存到哪里去的問題。但是用戶是如何實現(xiàn)登錄的? 是Forms認(rèn)證么?用到Cookie了么? Cookie里面有保存明文信息么(咳咳,最近某程旅游網(wǎng)好像很火?),接下來我們就來一一的回答這些問題。
在上一篇的例子中,我們可以簡單的發(fā)現(xiàn),要實現(xiàn)登錄實際上只有簡單的三行代碼
private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } }private async Task SignInAsync() { // 1. 利用ASP.NET Identity獲取用戶對象 var user = await UserManager.FindAsync("UserName", "Password"); // 2. 利用ASP.NET Identity獲取identity 對象 var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); // 3. 將上面拿到的identity對象登錄 AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity); }?我們發(fā)現(xiàn)UserManager.CreateIdentityAsync返回給我們的對象是一個ClaimsIdentity,這又是一個什么玩意?它和我們原來所熟知的Identity對象有什么關(guān)聯(lián)么?畢竟長的那么像,在深入之前,我們先要了解一下下面的概念。
什么是Claims-based(基于聲明)的認(rèn)證
首先這個玩意不是微軟特有的,Claims-based認(rèn)證和授權(quán)在國外被廣泛使用,包括微軟的ADFS,Google,Facebook等。?國內(nèi)我就不知道了,沒有使用過國內(nèi)的第三方登錄,有集成過QQ登錄或者支付寶登錄的同學(xué)可以解釋一下。
Claims-based認(rèn)證主要解決的問題?
對比我們傳統(tǒng)的Windows認(rèn)證和Forms認(rèn)證,claims-based認(rèn)證這種方式將認(rèn)證和授權(quán)與登錄代碼分開,將認(rèn)證和授權(quán)拆分成另外的web服務(wù)。活生生的例子就是我們的qq集成登錄,未必qq集成登錄采用的是claims-based認(rèn)證這種模式,但是這種場景,千真萬確就非常適合claims-based認(rèn)證。
Claims-based認(rèn)證的主要特點:
- 將認(rèn)證與授權(quán)拆分成獨立的服務(wù)
- 服務(wù)調(diào)用者(一般是網(wǎng)站),不需要關(guān)注你如何去認(rèn)證,你用Windows認(rèn)證也好,用令牌手機短信也好,與我無關(guān)。
- 如果用戶成功登錄的話,認(rèn)證服務(wù)(假如是QQ) 會返回給我們一個令牌。
- 令牌當(dāng)中包含了服務(wù)調(diào)用者所需要的信息,用戶名,以及角色信息等等。
總的來說就是,我再也不用管你怎么登錄,怎么樣去拿你有哪些角色了,我只需要把你跳到那個登錄站點上,然后它返回給我令牌信息,我從令牌上獲取需要的信息來確定你是誰,你擁有什么角色就可以了。
進一步理解Claims-based 認(rèn)證
為了讓大家進一步理解Claims-based認(rèn)證,我們從一個普通的登錄場景開始說起,拿QQ集成登錄來舉例。
簡單的來說,就是把登錄的代碼(驗證用戶,獲取用戶信息)拆分成獨立的服務(wù)或組件。
ASP.NET 下的 Claims-based認(rèn)證實現(xiàn)
說完什么是Claims-based認(rèn)證之后,我們接下來就可以看看ClaimsIdentity以及ClaimsPrincipal這兩個類,他們是.NET下Claims-based認(rèn)證的主要基石。當(dāng)然正如我們所想,他們繼承了接口IIdentity和IPrincipal。
IIdentity封裝用戶信息
這個接口很簡單,它只包含了三個最基本的用戶身份信息。
IPrincipal 代表著一個安全上下文
這個安全上下文對象包含了上面的identity以及一些角色和組的信息,每一個線程都會關(guān)聯(lián)一個Principal的對象,但是這個對象是屬性進程或者AppDomain級別的。ASP.NET自帶的 RoleProvider就是基于這個對象來實現(xiàn)的。
CalimsIdentity和ClaimsPrincipal
在System.Security.Claims命名空間下去,我們可以發(fā)現(xiàn)這兩個對象。下面我們來做一個小例子,這個小例子會告訴我們這兩個對象是如何進行認(rèn)證和授權(quán)的。我們要做的demo很簡單,建一個空的mvc站點,然后加上一個HomeController,和兩個Action。并且給這個HomeController打上Authroize的標(biāo)簽,但是注意我們沒有任何登錄的代碼,只有這個什么也沒有的Controller和兩個什么也沒有的Action。
當(dāng)然,結(jié)果也是可想而知的,我們得到了401頁面,因為我們沒有登錄。
下面我們就來實現(xiàn)登錄,這里的登錄非常簡單,我們手動去創(chuàng)建這個ClaimsIdentity和ClaimsPrincipal對象,然后將Principal對象指給當(dāng)前的HttpContext.Current.User。
我們在Global.asax中添加了Application_AuthenticateRequest方法,也就是每次MVC要對用戶進行認(rèn)證的時候都會進到我們這個方法里面,然后我們就這樣神奇的把用戶給登錄了。
當(dāng)然,我們沒有Home/Manager的訪問權(quán)限,因為我們上面只給了用戶Users的Role。
現(xiàn)在大家知道ClaimsIdentity和ClaimsPrincipal是如何使用了么?這里要注意一下的是,我們沒有設(shè)置IsAutheiticated為true,在.NET4.5以前,對于GenericIdentity只要設(shè)置它的Name的時候IsAutheiticated就自動設(shè)置為true了,而對于ClaimsIdentity是在它有了第一個Claim的時候。在.NET4.5以后,我們就可以靈活控制了,默認(rèn)ClaimsIdentity的IsAutheiticated是false,只有當(dāng)我們構(gòu)造函數(shù)中指定Authentication Type,它才為true。
最后結(jié)論,我們講了ClaimsIdentity什么的,講了這么多和今天的主題有嘛關(guān)系?我們上面說ASPNET Identity登錄有三句話,第一句話可以略過,第二句話就是我們上面講的。
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);UserManager實際上只是為我們創(chuàng)建了一個ClaimsIdentity的對象,還是通過我們自己從數(shù)據(jù)庫里面取出來的對象來創(chuàng)建的,它也就干了那么點事,一層小小的封裝而已。不要被后面的DefaultAuthenticationTypes.ApplicationCookie嚇到了,這里還沒有和cookie扯上半點關(guān)系,這就是一個字符串常量,和我們上面自己定義的MyClaimsLogin是沒有區(qū)別的。
到這里,我想算是把登錄代碼的第二句話講完了,講清楚了,那么我們來看看第三句話,也就是最后一句,其實它才是登錄的核心,第二句只是創(chuàng)建了一個ClaimsIdentity的對象。
private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity);通過F12查看,發(fā)現(xiàn)IAuthenticationManager 在??Microsoft.Owin.Security命名空間下,而這個接口是定義在Microsoft.OWin.dll中的。這又是個什么玩意兒?帶著這個疑問,我開始了我的OWin學(xué)習(xí)之旅。
到底什么是OWIN
首先我們來簡單介紹一下OWin,它是由微軟ASP.NET小組成員組織成立的一個開源項目。目標(biāo)是解耦服務(wù)器和應(yīng)用,這里面的服務(wù)器主要是指web 服務(wù)器,比如說IIS等,全稱是Open Web Interface for .Net。OWin可以說是一套定義,默認(rèn)它是沒有什么具體的實現(xiàn)的,那么在它的定義里面是如何實現(xiàn)服務(wù)器與應(yīng)用程序的解耦的呢? 我們又該如何理解服務(wù)器與應(yīng)用程序的解耦呢?
下面是個人的理解,拋磚引玉,希望大家多探討。
問題引入:?為什么要解耦服務(wù)器與應(yīng)用程序??
既然是服務(wù)器和應(yīng)用程序的解耦,那么這肯定是我們第一個應(yīng)該考慮的問題。我們先來簡單復(fù)習(xí)一下ASP.NET 或者是IIS 集成模式管道模型,也就是說一個http請求在進入IIS之后 (我們這里指7.0及以后版本的集成模式),一直到返回response這中間所經(jīng)歷的步驟。
大家知道,我們可以開發(fā)自己的Http Module去注冊這些事件,然后做相應(yīng)的處理。比如說FormsAuthenticationModule就是注冊了AuthenticateRequest事件,然后在這里面去檢查用戶的cookie信息來判斷用戶是否登錄的,這里就是一個典型的應(yīng)用程序與服務(wù)器之間的交互問題。而這些事件最后是被IIS觸發(fā)的,我們是通過web.config把我們自定義的http module注冊進了iis。回到我們的問題,如果我們的網(wǎng)站不運行在iis了,我們自己開發(fā)的這些Http module還能使用么?
另外的問題就是,大家知道我們在ASP.NET 里面經(jīng)常用到HttpContext,HttpApplicationt等對象,而ASP.NET所有的處理基本上都離不開這兩個對象,因為我們的Request以及Response都是封裝在HttpContext里面的,而這些信息是從IIS中來,最后也是交給IIS處理,因為微軟給IIS寫代碼的時候直接集成了這一塊,但是想一下,如果web服務(wù)器不是IIS,那么這些信息又從哪里獲取呢??
為什么需要解耦,是因為他們彼此之間的依懶過大,從而導(dǎo)致我們不能夠輕易的換掉其中任何一個。 即使現(xiàn)在,在web.config添加自己定義的http module 也不是一件能讓人開心的事情,反正我一想到那個很長的類名以及程序集名就夠蛋疼的。
顯然,很多人已經(jīng)開始意識到,在如今web飛快發(fā)展的年代,這種模式已經(jīng)不能夠滿足靈活多變的需求。越是輕量級,組件化的東西,越能夠快速適應(yīng)變化,更何況.NET現(xiàn)在要吸引開源社區(qū)的注意,只有把這一塊打通了,越來越多的強大的開源組件才能夠出現(xiàn)在.NET的世界里,比如說寫一個開源的ASP.NET web服務(wù)器。
OWin如何做到解耦
我們上面說Owin是一套定義,它通過將服務(wù)器與應(yīng)用程序之間的交互歸納為一個方法簽名,稱之為“應(yīng)用程序代理(application delegate)”
AppFunc = Func<IDictionary<string, object>, Task>;在一個基于Owin的應(yīng)用程序中的每一個組件都可以通過這樣的一個代理來與服務(wù)器進行交互。 這們這里的交互其實是與服務(wù)器一起來處理http request,比如說ASP.NET管理模型中的那些事件,認(rèn)證,授權(quán),緩存等等,原先我們是通過自定義的http module,在里面拿到包含了request和response的HttpContext對象,進行處理。而現(xiàn)在我們能拿到的就是一個Dictionary。
可是別小看了這個Dictionary,我們所有的信息比如Application state, request state,server state等等這些信息全部存在這個數(shù)據(jù)結(jié)構(gòu)中。這個dictionary會在Owin處理request的管道中進行傳遞,沒錯有了OWin之后,我們就不再是與ASP.NET 管道打交道了,而是OWin的管道,但是這個管道相對于ASP.NET 管道而言更靈活,更開放。
這個字典在OWin管道的各個組件中傳輸時,你可以任意的往里面添加或更改數(shù)據(jù)。 OWin默認(rèn)為我們定義了以下的數(shù)據(jù):
有了這些數(shù)據(jù)以后,我們就不需要和.NET的那些對象打交道了,比如說ASP.NET MVC中的HttpContextBase, 以及WEB API ?中的HttpRequestMessage和HttpResponseMessage。我們也不需要再考慮system.web 這個dll里的東西,我們只需要通過OWin就可以拿到我們想要的信息,做我們想做的事了。而OWin,它本身和web服務(wù)器或者IIS沒有任何關(guān)系。
微軟對OWin的開源實現(xiàn)Katana
我們上面講到了OWin只是一套定義,它本身沒有任何代碼,我們可以把它看成是微軟對外公開的一套標(biāo)準(zhǔn)。那么我們用到的Microsoft.OWin,這些dll又是從哪里來的呢? 好消息是它是開源的,代碼我們可以從CodePlex上下載,壞消息是它現(xiàn)在還沒有比較全的文檔,可能是我暫時還沒有找到。
它包括下面4個組件:
- Host: 托管我們應(yīng)用程序的進程,或者宿主,可以是IIS,可以我們自己寫的程序等。主要是用來啟動,加載OWin組件,以及合理的關(guān)閉他們
- Server: 這個Server就是用來暴露TCP端口,維護我們上面講到的那個字典數(shù)據(jù),然后通過OWin管理處理http請求
- Middleware :?這個中間件就是用來在OWin管道中處理請求的組件,你可以把它想象成一個自定義的httpModule,它會被注冊到OWin管道中一起處理http request
- Application:?這個最好理解,就我們自己開發(fā)的那個應(yīng)用程序或者說是網(wǎng)站
也就是說我們用到的Microsoft.OWin,那一系列的dll實現(xiàn)上是叫Katana(武士刀)象征著快,狠,準(zhǔn)!下面來一些名詞解釋,是一些簡單的概念有助于大家理解我們下面要講的內(nèi)容(ASP.NET Identity是如何借助 OWin來實現(xiàn)登錄的)。
OWin Application( OWin 應(yīng)用程序 )
這個程序引入了OWin的dll,同時會使用OWin中的一些組件完成對request的一些處理,比如說我們下面要講的OWin 認(rèn)證。
OWin 組件
我們也可能管它叫中間件,它通過暴露一個應(yīng)用程序代理,也就是接收一個IDictionary<string,object>,返回一個Task來參與到OWin對request和處理管道中。
Start up 類
每一個OWin的應(yīng)用程序都需要有一個start up的類,用來聲明我們要使用的OWin組件(即中間件)。Start up 類有以下幾種聲明方式:
?OWin authentication
Owin的很大亮點之一就是它可以讓我們的ASP.NET 網(wǎng)站擺脫IIS,但是畢竟大多數(shù)的ASP.NET 網(wǎng)站還是host在IIS上的,所以Katana項目還支持在IIS集成模式中運行Owin組件。 我們只需要在我們的項目中加上Microsoft.Owin.Host.SystemWeb這個包就可以了,其實默認(rèn)MVC5程序已經(jīng)為我們加上了。我們在VS2013中新建一個MVC5的站點,默認(rèn)會為我們加上以下的dll:
- OWin.dll
- Microsoft.Owin.dll
- Microsoft.Owin.Host.SystemWeb
- Microsoft.Owin.Security
- Microsoft.Owin.Security.Cookie
他們對應(yīng)nuget中的package:
這就是為什么我們可以拿到Microsoft.Owin.Security.IAuthenticationManager,然后再調(diào)用其 SignIn方法和SignOut方法。除了多了這些dll以外,VS還自動幫我們移除了FormsAuthenticationModule。
Forms 認(rèn)證
我們來小小的復(fù)雜一下Forms認(rèn)證,在Forms認(rèn)證中我們檢測完用戶名和密碼之后,只需要調(diào)用下面的代碼就會為我們創(chuàng)建用戶cookie。
FormsAuthentication.SetAuthCookie("Jesse", false);然后FormsAuthenticateionModule會在ASP.NET 管道的 AuthenticateRequest 階段去檢查是否有這個cookie,并把它轉(zhuǎn)換成我們需要的identity對象,這樣的話我們就不需要每一次都讓用戶去輸入用戶名和密碼了。所以登錄的過程實現(xiàn)上是這樣的。
Forms認(rèn)證有以下幾不足:
我們上面Forms的登錄過程,對于OWin登錄來說同樣適用。我們在上面講ASP.NET Identity登錄第二句話的時候已經(jīng)拿到了ClaimsIdentity,那么我們接下來要看的問題就是如何借助于IAuthenticationManager 去登錄? FormsAuthenticationModuel沒有了,誰來負(fù)責(zé)檢測cookie?您請接著往下看!
MVC 5默認(rèn)的start up配置類
VS除了為我們引用OWin相關(guān)dll,以及移除FormsAuthenticationModule以外,還為我們在App_Start文件夾里添加了一個Startup.Auth.cs的文件。
public partial class Startup {public void ConfigureAuth(IAppBuilder app){// 配置Middleware 組件app.UseCookieAuthentication(new CookieAuthenticationOptions{AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,LoginPath = new PathString("/Account/Login"),CookieSecure = CookieSecureOption.Never,});} }UseCookieAuthentication是一IAppBuilder 的一個擴展方法,定義在Microsoft.Owin.Security.Cookies.dll中。
CookieAuthenticationExtensions.cs
public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options) {if (app == null){throw new ArgumentNullException("app");}app.Use(typeof(CookieAuthenticationMiddleware), app, options);app.UseStageMarker(PipelineStage.Authenticate);return app; }將Owin Middleware綁定到IIS 集成模式的管道
UseCookieAuthentication主要調(diào)用了兩個方法:
- IAppBuilder.Use : 添加OWin middleware 組件到 OWin 管道
- IAppBuilder.UseStageMarker : 為前面添加的middleware指定在IIS 管道的哪個階段執(zhí)行。
PepelineStage這個枚舉定義和我們IIS管道的那些順序,也就是和我們Http Module里面可以綁定的那些事件是一樣的。
我們可以回顧一樣如何在http module中為Authenticate綁定事件。
public class MyModule : IHttpModule {public void Init(HttpApplication context){// 將ctx_AuthRequest 綁定的 AuthenticateRequest 事件context.AuthenticateRequest += ctx_AuthRequest;}void ctx_AuthRequest(object sender, EventArgs e){} }Owin這里的Use,貌似是借用了Node.js的用法呀- -! 不管怎么說,通過這樣一種方式,我們就可以將Owin 中間件注冊進IIS 集成模式的管道了。也就是說我們上面注冊的CookieAuthenticationMiddleware會在AuthenticaRequest 階段執(zhí)行。而它就是真正生成cookie以及讀取cookie的那只背后的手。
CookieAuthenticationMiddelware 負(fù)責(zé)讀取用戶信息cookie
如果你看的還算認(rèn)真的話,我們上面講claims-based認(rèn)證的時候有一個小例子。我們只需要在AuthenticateRequest階段將ClaimsPrincipal賦給當(dāng)前的User對象就可以實現(xiàn)登錄了,而這也是IAuthenticationManager.SignIn所做的。但是我們上面講Forms登錄的過程一樣,用戶登錄之后,我們需要生成cookie,這樣用戶下次訪問的時候就不需要登錄了,我們在Authenticate Request去檢測有沒有這個cookie就可以了,CookieAuthenticationMiddleware就負(fù)責(zé)做了這兩件事情。
我們可以到Katana的站點去下載源碼,然后找到CookieAuthenticationMiddleware這個類,然后找到最后生成cookie和讀取cookie的類:CookieAuthenticationHandler。
在CookieAuthenticationMiddleware中有兩個方法:
- AuthenticateCoreAsync : 從request中讀取cookie值,附給到identity對象,沒有什么內(nèi)幕,就是讀讀cookie進行解密,轉(zhuǎn)成identity對象。
- ApplyResponseGrantAsync : 往response中寫入cookie值,同樣沒有什么內(nèi)幕,有興趣的同學(xué)可以下載katana源碼瞅瞅。
CookieAuthenticationMiddelware 對cookie的加密方式
在我們上篇文章中對ASP.NET Identity登錄的例子中,如果你登錄了,那么你會發(fā)現(xiàn)我們的cookie是經(jīng)過加密的。而cookie的名稱是以.AspNet.為前綴加上我們
Startup中配置的AuthenticationType。
?
??
那么接下來,我們就來看一下CookieAuthenticationMiddleware是以什么樣的加密方式將我們的identity信息加密的,我們能不能將它解回來呢?
欲知后事如何,請聽下回分解~ ?
參考&小結(jié)
這一篇文章涉及到的新東西比較多,也花了我不少的時間。但是總的來說收獲還是蠻大的,把Claims-based總結(jié)性的概括了一下,然后又開始了Owin的學(xué)習(xí)之旅。越來越發(fā)現(xiàn).NET的強大,在開源社區(qū)的不斷貢獻下.NET也逐漸開始綻放出新的生命力。后面還會繼續(xù)Owin的學(xué)習(xí),有興趣的朋友可以繼續(xù)關(guān)注!還是我一直強調(diào)的,雖然ASP.NET Identity登錄只有三行代碼,但是背后卻隱藏的如此之深,如果你不懷著一顆好奇以及好學(xué)的心,你永遠(yuǎn)不知道背后有多么美麗的故事。如果只是習(xí)慣了使用框架,而不去了解它,那就會迷失在學(xué)習(xí)新技術(shù)的路上。但是如果你知道它的設(shè)計原理,你就會發(fā)現(xiàn)其實都差不多的,思想就是那么一套,但是外觀卻可能隨時改變。 另外的話我覺得這篇文章寫的不錯,值得點贊,你覺得呢?
我是Jesse Liu,關(guān)注我,跟我一起探尋.NET 那些新鮮,好玩,以及背后的故事吧 :)?
參考資料:
- http://owin.org/
- http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
- http://www.asp.net/aspnet/overview/owin-and-katana/an-overview-of-project-katana
- http://www.asp.net/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline
- http://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection
- http://msdn.microsoft.com/en-us/library/ff359101.aspx
總結(jié)
以上是生活随笔為你收集整理的MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVA 11090 Going in C
- 下一篇: 设计模式--6大原则--单一职责原则