使用Forms Authentication實現用戶註冊、登錄
一 基礎知識
本文示例代碼:http://www.codeplex.com/a/Release/ProjectReleases.aspx?ReleaseId=9518
前言
本來使用Forms Authentication進行用戶驗證的方式是最常見的,但系統地闡明其方法的文章並不多見,網上更多的文章都是介紹其中某一部分的使用方法或實現原理,而更多的朋友則發文詢問如何從頭到尾完整第實現用戶的註冊、登錄。因此,本文計劃通過一個實際的例子,介紹如何基於Forms Authentication實現:
- 用戶註冊(包括密碼的加密存儲)
- 用戶登錄(包括密碼的驗證、設置安全Cookie)
- 用戶實體替換(使用自己的類型作爲HttpContext.User的類型)
有關Forms Authentication的原理等內容不屬於本文的討論範疇,大家可以通過在搜索引擎中輸入“Forms Authentication”、“Forms身份驗證”、“窗體身份驗證”等關鍵詞來查看更多資源。本文僅從實用的角度介紹如何使用這一技術。
不使用Membership
本文介紹的實現方式不依賴ASP.NET 2.0提供的Membership功能。這主要是因爲,如果使用Membership,就必須用aspnet_regsql.exe實用工具配置數據庫,否則就得自己寫自定義的MembershipProvider。
如果用aspnet_regsql.exe配置數據庫,就會導致數據庫中出現很多我們實際並不需要的表或字段。此外更重要的是,默認的SqlMembershipProvider給很多數據表添加了ApplicationID列,其初衷可能是希望可以將多個應用程序的用戶全部放在一個庫裏,但又能彼此隔離。但實際情況是,每個應用程序都在其自身的數據庫中保存用戶數據。因此,引入這個ApplicationID無端地在每次查找用戶時增加了額外的條件。
另一方面,如果考慮自己實現一個MembershipProvider,因爲工作量巨大,有點得不償失。
但是,如果不使用Membership,也就無法享受ASP.NET 2.0中新增的Login等控件的便利了。
與Forms Authentication相關的配置
在web.config文件中,<system.web>/<authentication>配置節用於對驗證進行配置。爲<authentication>節點提供mode="Forms"屬性可以啓用Forms Authentication。一個典型的<authentication>配置節如下所示:
<authentication mode="Forms"> <forms name=".ASPXAUTH" loginUrl="login.aspx" defaultUrl="default.aspx" protection="All" timeout="30" path="/" requireSSL="false" slidingExpiration="false" enableCrossAppRedirects="false" cookieless="UseDeviceProfile" domain="" /> </authentication>
以上代碼使用的均是默認設置,換言之,如果你的哪項配置屬性與上述代碼一致,則可以省略該屬性。例如<forms name="MyAppAuth" />。下面依次介紹一下各種屬性:
- name——Cookie的名字。Forms Authentication可能會在驗證後將用戶憑證放在Cookie中,name屬性決定了該Cookie的名字。通過FormsAuthentication.FormsCookieName屬性可以得到該配置值(稍後介紹FromsAuthentication類)。
- loginUrl——登錄頁的URL。通過FormsAuthentication.LoginUrl屬性可以得到該配置值。當調用FormsAuthentication.RedirectToLoginPage()方法時,客戶端請求將被重定向到該屬性所指定的頁面。loginUrl的默認值爲“login.aspx”,這表明即便不提供該屬性值,ASP.NET也會嘗試到站點根目錄下尋找名爲login.aspx的頁面。
- defaultUrl——默認頁的URL。通過FormsAuthentication.DefaultUrl屬性得到該配置值。
- protection——Cookie的保護模式,可取值包括All(同時進行加密和數據驗證)、Encryption(僅加密)、Validation(僅進行數據驗證)和None。爲了安全,該屬性通常從不設置爲None。
- timeout——Cookie的過期時間。
- path——Cookie的路徑。可以通過FormsAuthentication.FormsCookiePath屬性得到該配置值。
- requireSSL——在進行Forms Authentication時,與服務器交互是否要求使用SSL。可以通過FormsAuthentication.RequireSSL屬性得到該配置值。
- slidingExpiration——是否啓用“彈性過期時間”,如果該屬性設置爲false,從首次驗證之後過timeout時間後Cookie即過期;如果該屬性爲true,則從上次請求該開始過timeout時間才過期,這意味着,在首次驗證後,如果保證每timeout時間內至少發送一個請求,則Cookie將永遠不會過期。通過FormsAuthentication.SlidingExpiration屬性可以得到該配置值。
- enableCrossAppRedirects——是否可以將以進行了身份驗證的用戶重定向到其他應用程序中。通過FormsAuthentication.EnableCrossAppRedirects屬性可以得到該配置值。爲了安全考慮,通常總是將該屬性設置爲false。
- cookieless——定義是否使用Cookie以及Cookie的行爲。Forms Authentication可以採用兩種方式在會話中保存用戶憑據信息,一種是使用Cookie,即將用戶憑據記錄到Cookie中,每次發送請求時瀏覽器都會將該Cookie提供給服務器。另一種方式是使用URI,即將用戶憑據當作URL中額外的查詢字符串傳遞給服務器。該屬性有四種取值——UseCookies(無論何時都使用Cookie)、UseUri(從不使用Cookie,僅使用URI)、AutoDetect(檢測設備和瀏覽器,只有當設備支持Cookie並且在瀏覽器中啓用了Cookie時才使用Cookie)和UseDeviceProfile(只檢測設備,只要設備支持Cookie不管瀏覽器是否支持,都是用Cookie)。通過FormsAuthentication.CookieMode屬性可以得到該配置值。通過FormsAuthentication.CookiesSupported屬性可以得到對於當前請求是否使用Cookie傳遞用戶憑證。
- domain——Cookie的域。通過FormsAuthentication.CookieDomain屬性可以得到該配置值。
以上針對<system.web>/<authentication>/<forms>節點的介紹非常簡略,基本上是對於文檔進行的額外說明。有關<forms>節點的更多說明,請參見MSDN文檔(http://msdn2.microsoft.com/zh-cn/library/1d3t3c61(VS.85).aspx)。
FormsAuthentication類
FormsAuthentication類用於輔助我們完成窗體驗證,並進一步完成用戶登錄等功能。該類位於system.web.dll程序集的System.Web.Security命名空間中。通常在Web站點項目中可以直接使用這個類,如果是在類庫項目中使用這個類,請確保引用了system.web.dll。
前一節已經介紹了FormsAuthentication類的所有屬性。這一節將介紹該類少數幾個常用的方法。
RedirectToLoginPage方法用於從任何頁面重定向到登錄頁,該方法有兩種重載方式:
public static void RedirectToLoginPage () public static void RedirectToLoginPage (string extraQueryString)
兩種方式均會使瀏覽器重定向到登錄頁(登錄頁的URL由<forms>節點的loginUrl屬性指出)。第二種重載方式還能夠提供額外的查詢字符串。
RedirectToLoginPage通常在任何非登錄頁的頁面中調用。該方法除了進行重定向之外,還會向URL中附加一個ReturnUrl參數,該參數即爲調用該方法時所在的頁面的URL地址。這是爲了方便登錄後能夠自動回到登錄前所在的頁面。
RedirectFromLoginPage方法用於從登錄頁跳轉回登錄前頁面。這個“登錄前”頁面即由訪問登錄頁時提供的ReturnUrl參數指定。如果沒有提供ReturnUrl參數(例如,不是使用RedirectToLoginPage方法而是用其他手段重定向到或直接訪問登錄頁時),則該方法會自動跳轉到由<forms>節點的defaultUrl屬性所指定的默認頁。
此外,如果<forms>節點的enableCrossAppRedirects屬性被設置爲false,ReturnUrl參數所指定的路徑必須是當前Web應用程序中的路徑,否則(如提供其他站點下的路徑)也將返回到默認頁。
RedirectFromLoginPage方法有兩種重載形式:
public static void RedirectFromLoginPage (string userName, bool createPersistentCookie) public static void RedirectFromLoginPage (string userName, bool createPersistentCookie, string strCookiePath)
userName參數表示用戶的標識(如用戶名、用戶ID等);createPersistentCookie參數表示是否“記住我”;strCookiePath參數表示Cookie路徑。
RedirectFromLoginPage方法除了完成重定向之外,還會將經過加密(是否加密取決於<forms>節點的protection屬性)的用戶憑據存放到Cookie或Uri中。在後續訪問中,只要Cookie沒有過期,則將可以通過HttpContext.User.Identity.Name屬性得到這裏傳入的userName屬性。
此外,FormsAuthentication還有一個SignOut方法,用於完成用戶註銷。其原理是從Cookie或Uri中移除用戶憑據。
小結
好了,至此所需要掌握的基礎知識就齊備了,接下來我們將實現用戶註冊、登錄等功能。
二 用戶註冊與登錄
從這一部分開始,我們將通過一個實際的完整示例來看一下如何實現用戶註冊與登錄。在介紹註冊與登錄之前,我們首先介紹一下如何判斷用戶是否已登錄,並未後面的示例編寫一些基礎代碼。
判斷用戶是否已經登錄
首先,在Web站點項目中添加一個MasterPage,例如MasterPage.master。在這個母版頁的ContentPlaceHolder控件之前、<From>標籤之內插入如下代碼:
<asp:Panel ID="pnlAnonymous" runat="server"> <asp:LinkButton ID="btnLogin" runat="server" Text="登錄" OnClick="btnLogin_Click"> </asp:LinkButton> | <asp:HyperLink ID="lnkRegister" runat="server" NavigateUrl="~/register.aspx" Text="註冊"> </asp:HyperLink> </asp:Panel> <asp:Panel ID="pnlLoggedin" runat="server"> 歡迎您,<asp:Label ID="lblUserName" runat="server"></asp:Label>! [<asp:LinkButton ID="btnLogout" runat="server" Text="註銷" onclick="btnLogout_Click"></asp:LinkButton>] </asp:Panel> <asp:Panel ID="pnlNavigate" runat="server"> <asp:HyperLink ID="lnkDefault" runat="server" NavigateUrl="~/default.aspx" Text="首頁"> </asp:HyperLink> | <asp:HyperLink ID="lnkTest" runat="server" NavigateUrl="~/test.aspx" Text="測試頁"> </asp:HyperLink> </asp:Panel>
這裏提供了三個Panel控件——pnlAnonymous、pnlLoggedin和pnlNavigate。pnlAnonymous用於在用戶未登錄時顯示“登錄”和“註冊”鏈接;pnlLoggedin用於在用戶已登錄時顯示用戶信息(如用戶名和到用戶個人信息頁的鏈接等,這裏僅顯示用戶名),以及一個“註銷”按鈕;pnlNavigate在任何時候都顯示,是站點的導航欄。
現在我們要實現的是,判斷用戶是否登錄,並顯示pnlAnonymous和pnlLoggedin二者之一。這裏,如果是使用ASP.NET 2.0 Membership,則可以方便地使用LoginView、LoginName和LoginStatus等控件實現這些功能;然而我們不得不爲此忍受Membership所帶來的龐大而繁多的數據庫對象,或者花更多時間去編寫自定義的MembershipPorvider。
這一系列的第一部分曾介紹過,如果用戶已經登錄,則可以從HttpContext.User.Identity.Name得到已登錄用戶的標識(通常是用戶名)。然而,如果用戶未登錄,這個值則爲空字符串。因此,通過判斷該值是否爲空字符串,即可的值用戶是否已登錄。
由此,我們在MasterPage的後臺代碼中添加Page_Init方法,在頁面初始化時判斷用戶是否登錄,並顯示對應的Panel控件;此外,在用戶已登錄時,在pnlLoggedin中的歡迎語裏插入已登錄用戶的用戶名。
protected void Page_Init(object sender, EventArgs e) { // 判斷用戶是否已登錄。 if(HttpContext.Current.User.Identity.Name == "") { // 用戶未登錄。 pnlAnonymous.Visible = true; pnlLoggedin.Visible = false; } else { // 用戶已登錄。 pnlAnonymous.Visible = false; pnlLoggedin.Visible = true; lblUserName.Text = HttpContext.Current.User.Identity.Name; } }
此外,我們還向項目中添加了default.aspx、login.aspx、register.aspx、test.aspx這樣四個頁面。其中test.aspx用於檢驗重定向到登錄頁後的ReturnUrl參數值,和登錄後回到之前頁面的效果。
用戶註冊
用戶註冊部分並不屬於驗證的範疇,因此Forms Authentication也沒有提供過多的支持。然而用戶註冊是比較簡單的,只需通過一個頁面蒐集用戶信息,並存放到數據庫中即可。在這個示例中,爲了方便和突出真正要討論的內容,用戶信息並不是真的放到數據庫中的,而是放在一個集合裏,不過通過一個DataAccess類來隱藏這種差異。理論上,一旦關閉應用程序並重新開啓,放在集合中的數據就會丟失。然而實際上,再重複運行這裏的示例時,用戶數據並不會丟失,即便應用程序有所改動也是。這是因爲ASP.NET 2.0對於Web項目都是動態重新編譯的,應用程序從未直接終止過。
此外,就用戶的數據實體類而言,我們僅提供了註冊、登錄所必需的屬性——用戶名、密碼散列值、密碼Salt值。
爲了進行用戶註冊,我們建立了register.aspx頁面,該頁面僅負責蒐集用戶信息,真正的創建用戶動作在App_Code下的Membership類中完成。注意,此Membership非彼Membership,與ASP.NET 2.0 Membership沒有任何關係。
首先在Membership類中添加一個靜態方法CreateUser,用於創建用戶。該方法完成用戶實體的創建和密碼的散列等功能。其代碼如下所示:
public static void CreateUser(string userName, string password) { UserObject user = new UserObject(); user.Name = userName; user.PasswordSalt = GenerateSalt(); user.PasswordHash = EncodePassword(password, user.PasswordSalt); DataAccess.AddUser(user); }
這段代碼非常簡單,其中用到了GenerateSalt和EncodePassword方法,這兩個方法用於完成salt值的生成和密碼的加salt散列。其代碼抽取自MembershipProvider類,進行了精簡如下所示:
public const string PasswordHashAlgorithmName = "SHA1"; static string EncodePassword(string password, string salt) { byte[] src = Encoding.Unicode.GetBytes(password); byte[] saltbuf = Convert.FromBase64String(salt); byte[] dst = new byte[saltbuf.Length + src.Length]; byte[] inArray = null; Buffer.BlockCopy(saltbuf, 0, dst, 0, saltbuf.Length); Buffer.BlockCopy(src, 0, dst, saltbuf.Length, src.Length); HashAlgorithm algorithm = HashAlgorithm.Create(PasswordHashAlgorithmName); inArray = algorithm.ComputeHash(dst); return Convert.ToBase64String(inArray); } static string GenerateSalt() { byte[] data = new byte[0x10]; new RNGCryptoServiceProvider().GetBytes(data); return Convert.ToBase64String(data); }
然後在register.aspx頁面中創建蒐集用戶信息的表單,這個表單非常簡單,僅需在ContentPlaceHolder1內添加如下代碼:
<table> <tr> <td>用戶名:</td> <td><asp:TextBox ID="txtUserName" runat="server"></asp:TextBox></td> </tr> <tr> <td>密碼:</td> <td><asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox></td> </tr> <tr><td colspan="2"><asp:Button ID="btnOK" runat="server" Text="確定" onclick="btnOK_Click" /></td></tr> </table> <asp:Label ID="lblMessage" runat="server"></asp:Label>
注意這比實際的註冊頁要簡單許多,沒有對用戶名和密碼的格式做驗證,也沒有讓用戶重複輸入密碼以確保沒有鍵入錯誤。
在“確定”按鈕的事件處理器中添加如下代碼,完成註冊功能:
protected void btnOK_Click(object sender, EventArgs e) { try { Membership.CreateUser(txtUserName.Text, txtPassword.Text); lblMessage.Text = "用戶創建成功!"; } catch(Exception ex) { lblMessage.Text = "錯誤:" + ex.Message; } }
用戶登錄
用戶登錄大致由下面幾個步驟完成:
- 根據用戶名取得與用戶相關的信息(用戶實體對象);
- 判斷用戶密碼是否正確;
- 設置用戶憑據Cookie並重定向到登錄前頁面。
我們主要還是在Membership類中來完成這些工作。爲Membership類添加一個Login方法:
public static void Login(string userName, string password, bool rememberMe) { // 獲取用戶。 UserObject user = DataAccess.GetUserByName(userName); if(user == null) throw new ArgumentException("用戶不存在!", "UserName"); // 檢查密碼是否正確。 string pwdHash = EncodePassword(password, user.PasswordSalt); if(pwdHash != user.PasswordHash) throw new ArgumentException("密碼錯誤!", "Password"); // 設置安全Cookie並進行重定向。 FormsAuthentication.RedirectFromLoginPage(userName, rememberMe); }
這裏重點在於檢查密碼,不過其工作方式已經在上一篇文章中介紹過了,此處不再贅述。讀者應該注意到,對密碼進行散列使用的是已經存放在數據實體中的Salt值,而不是重新生成Salt值。
這段示例代碼通過拋異常來返回錯誤信息。讀者在實際編寫這個方法時,可以創建多個Exception類的派生類,分別表示不同的錯誤;或者採取其他無需拋異常的方式。這一點仁者見仁,智者見智。
接下來,提供一個簡單的登錄頁,完成收集用戶名和密碼的工作。login.aspx頁面的結構也很簡單,只需在<ContentPlaceHolder1>中添加下列代碼:
<table> <tr> <td>用戶名:</td> <td><asp:TextBox ID="txtUserName" runat="server"></asp:TextBox></td> </tr> <tr><td>密碼:</td> <td><asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox></td> </tr> <tr> <td colspan="2"> <asp:Button ID="btnLogin" runat="server" Text="登錄" onclick="btnLogin_Click" /> <asp:CheckBox ID="chkRememberMe" runat="server" Text="記住我" /> </td></tr> </table> <asp:Label ID="lblMessage" runat="server"></asp:Label>
最後,在“登錄”按鈕的事件處理器中添加如下代碼,調用剛剛寫好的Membership.Login方法,完成登錄。
protected void btnLogin_Click(object sender, EventArgs e) { try { Membership.Login(txtUserName.Text, txtPassword.Text, chkRememberMe.Checked); } catch(Exception ex) { lblMessage.Text = "錯誤:" + ex.Message; } }
至此,用戶登錄功能就OK了。由於之前已經完成了判斷用戶是否登錄的代碼,現在就可以運行示例,註冊一個用戶並嘗試從各個頁面進入登錄頁進行登錄了。
用戶註銷
至此,我們已經爲用戶進入我們的系統提供了寬敞大路;但作爲一個負責任的程序,我們還得確保當用戶離開我們的程序時,能夠“揮一揮衣袖”不留下一絲痕跡。這個安全離開的行爲,我們稱之爲“註銷”;要抹去的“痕跡”也就是登錄時留下的用戶憑據Cookie。
本文的第一部分已經介紹過,FormsAuthentication.SignOut方法可以協助完成這一任務。我們在MasterPage中也預先放置好了一個“註銷”鏈接按鈕,在這裏,只要爲該按鈕添加如下事件處理器即可:
protected void btnLogout_Click(object sender, EventArgs e) { FormsAuthentication.SignOut(); Response.Redirect(Request.RawUrl); }
在這裏,首先通過調用FormsAuthentication.SignOut方法移除了用戶憑證Cookie。這個方法並不能自動完成跳轉,因此我麼必須自己完成跳轉任務。關於註銷後跳轉到哪個頁面,每個應用程序都有自己的方式(如跳到登錄頁或跳到首頁等),這裏選擇的是刷新當前頁。
小結
好了,到現在爲止,完整的用戶註冊、登錄功能就都實現了。由於有了FormsAuthentication類,這一任務已經非常簡單了。這一部分僅僅是從實踐的角度順序操作了一下。
下一部分將介紹如何將我們自己的用戶實體放到HttpContext.User屬性中。
三 用戶實體替換
IPrincipal和IIdentity
通過查閱文檔,我們可以看到HttpContext.User屬性的類型是IPrincipal接口。然而我們知道,接口通常是不能直接訪問的,其背後必定隱藏了一個實現了該接口的對象。那麼這個實際對象的類型是什麼呢?
讓我們在前面示例的MasterPage的Page_Init方法上加一個斷點,再次運行程序,可以得到HttpContext.User屬性的真正類型是System.Security.Principal.GenericPrincipal。
查看IPrincipal接口的定義,可以看到它只有一個公開屬性——Identity,其類型是這裏要提到的另外一個重要接口IIdentity。通過上面的斷點跟蹤,我們還能知道對於GenericPrincipal而言,其Identity屬性的實際類型是GenericIdentity,也是位於System.Security.Principal命名空間中。
由此,我們引出了.NET Framework中關於Principal(實體)的整個類型系統。所有這些類型都位於mscorlib.dll程序集中,由此也可以看出,這套模型隸屬於整個系統的基石。
實現自己的IPrincipal
要想用自己的實體對象替換HttpContext.User,就必須讓自己的實體對象實現IPrincipal接口;通常還必須伴隨着實現IIdentity接口。
目前系統中有的是一個數據實體對象。一般而言,實現IPrincipal接口有一下兩種方式——
編寫單獨的類型實現IPrincipal接口,並在其中包含數據實體對象;修改數據實體對象使其實現IPrincipal接口。對於這兩種方式而言,其Identity屬性可以通過以下三種方式實現——
- 使用.NET Framework提供的GenericIdentity;
- 創建自定義的類,實現Identity接口;
- 修改數據實體對象或自定義的實體類,讓它們同時實現IPrincipal和IIdentity接口。
對於簡單的應用程序而言,通常可以修改數據實體對象,使其同時實現IPrincipal和IIdentity接口。而就複雜的分層架構應用程序,則建議在邏輯層創建分別實現了IPrincipal和IIdentity接口的對象。本文的示例明顯屬於前一種情況,因此我們考慮修改作爲數據實體類的UserObject類,讓其實現兩個接口。以下是修改完畢的UserObject類:
public class UserObject : IPrincipal, IIdentity { /// <summary> /// 用戶名。 /// </summary> public string Name; /// <summary> /// 密碼散列值。 /// </summary> public string PasswordHash; /// <summary> /// 密碼salt值。 /// </summary> public string PasswordSalt; #region IIdentity Members public string AuthenticationType { get { return "Froms"; } } public bool IsAuthenticated { get { return true; } } string IIdentity.Name { get { return this.Name; } } #endregion #region IPrincipal Members public IIdentity Identity { get { return this; } } public bool IsInRole(string role) { return false; } #endregion }
首先我們來看一下對IIdentity接口的實現。該接口要求三個屬性——AuthenticationType、IsAuthenticated和Name。AuthenticationType表示該用戶標識所使用的驗證類型,這裏返回的是“Forms”;IsAuthenticated屬性表示當前用戶是否已經通過驗證(即是否已登錄。在這個例子裏,我們只針對已登錄用戶進行實體替換,所以這個屬性總是返回true。通常,實際的Web應用程序編寫時還有一種習慣,就是爲未登錄用戶(稱之爲匿名用戶)也提供一個用戶實體對象,此時就需要爲IsAuthenticated提供邏輯,判斷用戶是否已通過驗證了。最後IIdentity接口還要求對象提供一個Name屬性,在這裏,由於已經存在了Name字段,因此才用“顯示接口實現”來提供Name屬性,返回對象自身的Name字段即可。
接下來我們看一下IPrincipal接口的實現。該接口要求提供一個Identity屬性和一個IsInRole方法。由於UserObject類本身已經實現了IIdentity接口,因此在Identity屬性中直接reutren this即可。因爲我們這個示例不涉及用戶分組(角色)方面的技術,因此IsInRole方法總是返回false。
用戶實體替換
用戶實體替換即使用我們自己編寫的類型的實例來替換HttpContext.User屬性。實體替換應該發生在HttpApplication的PostAuthenticateRequest事件發生時,因爲此時ASP.NET已經從客戶端得到了用戶憑證Cookie並進行了解密和校驗。
我們既可以編寫一個HttpModule來處理PostAuthenticateRequest事件,也可以在Global..asax文件中添加時間處理器。這裏爲了簡單,我們選擇在Global.asax中添加如下事件處理器:
void Application_PostAuthenticateRequest(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; if(app.Context.User.Identity.Name != "") // 僅在已登錄時替換 { UserObject user = DataAccess.GetUserByName(app.Context.User.Identity.Name); app.Context.User = user; Thread.CurrentPrincipal = user; } }
在這裏我們首先進行了判斷,如果用戶已登錄,才進行實體替換。當然你也可以選擇未未登錄用戶也提供一個匿名用戶實體。
接下來,我們通過本來已經存放在HttpContext.User.Identity中的用戶標識得到了數據實體對象,然後分別將其賦予HttpContext.User和Thread.CurrentPrincipal。
至此,我們的示例代碼就完工了。沒有提到的是,完成了這一步之後,你就可以通過類似下面的代碼在任何可以訪問到HttpContext的地方獲取用戶實體了:
UserObject user = HttpContext.Current.User as UserObject; if(user != null) { // 可以使用user } else { // 用戶未登錄 }
需要注意,由於在這裏我們僅對已登錄用戶進行了用戶實體替換,所以代碼使用as關鍵字進行類型轉換並結合if語句進行判斷是必需的。
小結
好吧,這一部分說的是用戶實體替換。