HttpModule是用來註冊HttpApplication事件的,實現IHttpModule介面的托管代碼模塊可以訪問該請求管道的所有事件。那麼對於我們最常用的ASP.NET Forms身份驗證模塊是如何底層封裝處理的呢? ...
HttpModule是用來註冊HttpApplication事件的,實現IHttpModule介面的托管代碼模塊可以訪問該請求管道的所有事件。那麼對於我們最常用的ASP.NET Forms身份驗證模塊是如何底層封裝處理的呢?
今天過了一遍ASP.NET生命周期,以前的時候喜歡做各種應用,小程式等,漸漸地就覺得真沒意思,因為只要你懂點基本的語法,會用相關的庫亦或是框架就行,如果出錯就是些許的細節錯誤,嚴格來說這不鍛煉人,這有點像是溫水煮青蛙,當然不能說這不好,這可以幫我們熟練地掌握框架的使用,增加熟練度及相關基礎的應用,但是就個人而言老覺得缺點什麼...後來想想,我要做的其實就是讓別人用我開發的框架,庫,我想研究的是框架底層的架構而不是用框架。於是過了一遍生命周期,處了IIS處理請求部分實在不懂之外,對ASP.NET處理請求還是更熟練了,對於不懂得我不會去刻意強求懂,畢竟自己的技術深度,廣度擺在那,日後到了時候自然會懂。IIS7較之於之前的版本,其擴增了一個集成模式。IS 7.0 集成管道是一種統一的請求處理管道,它同時支持本機代碼和托管代碼模塊。實現 IHttpModule 介面的托管代碼模塊可訪問該請求管道中的所有事件。例如,托管代碼模塊可用於 ASP.NET 網頁(.aspx 文件)和 HTML 頁(.htm 或 .html 文件)的 ASP.NET Forms 身份驗證。即使 IIS 和 ASP.NET 將 HTML 頁視為靜態資源,情況也是如此。
從功能上講,HttpModule之於ASP.NET,就好比ISAPI Filter之於IIS一樣。IIS將接收到的請求分發給相應的ISAPI Extension之前,註冊的ISAPI Filter會先截獲該請求。ISAPI Filter可以獲取甚至修改請求的內容,完成一些額外的功能。與之相似地,當請求轉入ASP.NET管道後,最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象,但是在Handler正式工作之前,ASP.NET會先載入並初始化所有配置的HttpModule對象。HttpModule在初始化的過程中,會將一些功能註冊到HttpApplication相應的事件中,那麼在HttpApplication整個請求處理生命周期中的某個階段,相應的事件會被觸發,通過HttpModule註冊的事件處理程式也得以執行。
所有的HttpModule都實現了IHttpModule介面,下麵是IHttpModule的定義。其中Init方法用於實現HttpModule自身的初始化,該方法接受一個HttpApplication對象,有了這個對象,事件註冊就很容易了。
ASP.NET提供的很多基礎構件(Infrastructure)功能都是通過相應的HttpModule實現的,下麵類列出了一些典型的HttpModule:
OutputCacheModule:實現了輸出緩存(Output Caching)的功能;
SessionStateModule:在無狀態的HTTP協議上實現了基於會話(Session)的狀態;
WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthentication- Module:實現了3種典型的身份認證方式:Windows認證、Forms認證和Passport認證;
UrlAuthorizationModule + FileAuthorizationModule:實現了基於Uri和文件ACL(Access Control List)的授權。
抱著吹毛求疵的學習態度,我研究了一下Forms認證的源碼(其實也不是源碼,利用reflector查出來的)
看下FormsAuthenticationModule的源碼:
看下我們最熟的Init方法:
可以看到,在這裡給我們註冊了兩個HttpApplication管道事件,我們看看AuthenticateRequest事件給我們的解釋:
然後我們看看OnEnter這個方法:
隨後我們點進去看看OnAuthenticate方法:
private void OnAuthenticate(FormsAuthenticationEventArgs e) { HttpCookie cookie = null; if (this._eventHandler != null) { this._eventHandler(this, e); } if (e.Context.User == null) { if (e.User != null) { e.Context.SetPrincipalNoDemand(e.User); } else { bool cookielessTicket = false; FormsAuthenticationTicket tOld = ExtractTicketFromCookie(e.Context, FormsAuthentication.FormsCookieName, out cookielessTicket); if ((tOld != null) && !tOld.Expired) { FormsAuthenticationTicket ticket = tOld; if (FormsAuthentication.SlidingExpiration) { ticket = FormsAuthentication.RenewTicketIfOld(tOld); } e.Context.SetPrincipalNoDemand(new GenericPrincipal(new FormsIdentity(ticket), new string[0])); if (!cookielessTicket && !ticket.CookiePath.Equals("/")) { cookie = e.Context.Request.Cookies[FormsAuthentication.FormsCookieName]; if (cookie != null) { cookie.Path = ticket.CookiePath; } } if (ticket != tOld) { if ((cookielessTicket && (ticket.CookiePath != "/")) && (ticket.CookiePath.Length > 1)) { ticket = FormsAuthenticationTicket.FromUtc(ticket.Version, ticket.Name, ticket.IssueDateUtc, ticket.ExpirationUtc, ticket.IsPersistent, ticket.UserData, "/"); } string cookieValue = FormsAuthentication.Encrypt(ticket, !cookielessTicket); if (cookielessTicket) { e.Context.CookielessHelper.SetCookieValue('F', cookieValue); e.Context.Response.Redirect(e.Context.Request.RawUrl); } else { if (cookie != null) { cookie = e.Context.Request.Cookies[FormsAuthentication.FormsCookieName]; } if (cookie == null) { cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath }; } if (ticket.IsPersistent) { cookie.Expires = ticket.Expiration; } cookie.Value = cookieValue; cookie.Secure = FormsAuthentication.RequireSSL; cookie.HttpOnly = true; if (FormsAuthentication.CookieDomain != null) { cookie.Domain = FormsAuthentication.CookieDomain; } e.Context.Response.Cookies.Remove(cookie.Name); e.Context.Response.Cookies.Add(cookie); } } } } } }
留心的話,可以發現在這個方法裡面所有與Forms表單認證相關的類都涉及到了。因此對於Forms表單認證的處理模塊,最重要的就是這個FormsAuthenticationModule類了,在這裡面,會把為瞭解耦操作所創建的類都給用上。不得不說,要我寫寫不出來,理解下HttpModule管道的實際應用還是可以的,對模塊設計有個大概的瞭解。在這裡,這個類不知道會不會讓你想起ASP.NET MVC框架下的Authentication Filter這個過濾器,過濾器的實現其實就是利用了Attribute這個特性才實現AOP切麵註入,因此,其實這個也應該可以加上Attribute來實現AOP。,當然這是我的猜想哈,不過應該可行。