ASP.NET MVC - 設置自定義 IIdentity 或 IPrincipal
我需要做一些相當簡單的事情:在我的 ASP.NET MVC 應用程序中,我想設置一個自定義的 IIdentity / IPrincipal。哪個更容易/更合適。我想擴展預設值,以便我可以呼叫類似
User.Identity.Idand的東西User.Identity.Role。沒什麼特別的,只是一些額外的屬性。我已經閱讀了大量的文章和問題,但我覺得我讓它變得比實際更難。我以為這很容易。如果使用者登錄,我想設置自定義 IIdentity。所以我想,我將
Application_PostAuthenticateRequest在我的 global.asax 中實現。但是,在每個請求上都會呼叫它,並且我不想在每個請求上都呼叫數據庫,因為它會從數據庫中請求所有數據並放入自定義 IPrincipal 對象。這似乎也非常不必要,很慢,而且在錯誤的地方(在那裡進行數據庫呼叫),但我可能是錯的。或者這些數據還來自哪裡?所以我想,每當使用者登錄時,我都可以在我的會話中添加一些必要的變數,我將它們添加到
Application_PostAuthenticateRequest事件處理程序中的自定義 IIdentity 中。但是,我Context.Session的null存在,所以這也不是要走的路。我已經為此工作了一天,我覺得我錯過了一些東西。這應該不難做到,對吧?我也對隨之而來的所有(半)相關內容感到有些困惑。
MembershipProvider,MembershipUser,RoleProvider,ProfileProvider,IPrincipal,IIdentity,FormsAuthentication…. 我是唯一一個覺得這一切都非常混亂的人嗎?如果有人能告訴我一個簡單、優雅、高效的解決方案,可以在 IIdentity 上儲存一些額外的數據,而不需要額外的模糊……那就太好了!我知道關於 SO 有類似的問題,但如果我需要的答案在那裡,我一定忽略了。
這是我的做法。
我決定使用 IPrincipal 而不是 IIdentity,因為這意味著我不必同時實現 IIdentity 和 IPrincipal。
- 創建介面
interface ICustomPrincipal : IPrincipal { int Id { get; set; } string FirstName { get; set; } string LastName { get; set; } }
- 自定義主體
public class CustomPrincipal : ICustomPrincipal { public IIdentity Identity { get; private set; } public bool IsInRole(string role) { return false; } public CustomPrincipal(string email) { this.Identity = new GenericIdentity(email); } public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
- CustomPrincipalSerializeModel - 用於將自定義資訊序列化到 FormsAuthenticationTicket 對像中的 userdata 欄位中。
public class CustomPrincipalSerializeModel { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
- 登錄方法 - 使用自定義資訊設置 cookie
if (Membership.ValidateUser(viewModel.Email, viewModel.Password)) { var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First(); CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel(); serializeModel.Id = user.Id; serializeModel.FirstName = user.FirstName; serializeModel.LastName = user.LastName; JavaScriptSerializer serializer = new JavaScriptSerializer(); string userData = serializer.Serialize(serializeModel); FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket( 1, viewModel.Email, DateTime.Now, DateTime.Now.AddMinutes(15), false, userData); string encTicket = FormsAuthentication.Encrypt(authTicket); HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); Response.Cookies.Add(faCookie); return RedirectToAction("Index", "Home"); }
- Global.asax.cs - 讀取 cookie 並替換 HttpContext.User 對象,這是通過覆蓋 PostAuthenticateRequest 來完成的
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e) { HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; if (authCookie != null) { FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); JavaScriptSerializer serializer = new JavaScriptSerializer(); CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData); CustomPrincipal newUser = new CustomPrincipal(authTicket.Name); newUser.Id = serializeModel.Id; newUser.FirstName = serializeModel.FirstName; newUser.LastName = serializeModel.LastName; HttpContext.Current.User = newUser; } }
- 在 Razor 視圖中訪問
@((User as CustomPrincipal).Id) @((User as CustomPrincipal).FirstName) @((User as CustomPrincipal).LastName)在程式碼中:
(User as CustomPrincipal).Id (User as CustomPrincipal).FirstName (User as CustomPrincipal).LastName我認為程式碼是不言自明的。如果不是,請告訴我。
此外,為了使訪問更容易,您可以創建一個基本控制器並覆蓋返回的使用者對象 (HttpContext.User):
public class BaseController : Controller { protected virtual new CustomPrincipal User { get { return HttpContext.User as CustomPrincipal; } } }然後,對於每個控制器:
public class AccountController : BaseController { // ... }這將允許您訪問程式碼中的自定義欄位,如下所示:
User.Id User.FirstName User.LastName但這在視圖內部不起作用。為此,您需要創建一個自定義 WebViewPage 實現:
public abstract class BaseViewPage : WebViewPage { public virtual new CustomPrincipal User { get { return base.User as CustomPrincipal; } } } public abstract class BaseViewPage<TModel> : WebViewPage<TModel> { public virtual new CustomPrincipal User { get { return base.User as CustomPrincipal; } } }使其成為 Views/web.config 中的預設頁麵類型:
<pages pageBaseType="Your.Namespace.BaseViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> </namespaces> </pages>在視圖中,您可以像這樣訪問它:
@User.FirstName @User.LastName