Asp.net

ASP.NET MVC - 設置自定義 IIdentity 或 IPrincipal

  • June 30, 2009

我需要做一些相當簡單的事情:在我的 ASP.NET MVC 應用程序中,我想設置一個自定義的 IIdentity / IPrincipal。哪個更容易/更合適。我想擴展預設值,以便我可以呼叫類似User.Identity.Idand的東西User.Identity.Role。沒什麼特別的,只是一些額外的屬性。

我已經閱讀了大量的文章和問題,但我覺得我讓它變得比實際更難。我以為這很容易。如果使用者登錄,我想設置自定義 IIdentity。所以我想,我將Application_PostAuthenticateRequest在我的 global.asax 中實現。但是,在每個請求上都會呼叫它,並且我不想在每個請求上都呼叫數據庫,因為它會從數據庫中請求所有數據並放入自定義 IPrincipal 對象。這似乎也非常不必要,很慢,而且在錯誤的地方(在那裡進行數據庫呼叫),但我可能是錯的。或者這些數據還來自哪裡?

所以我想,每當使用者登錄時,我都可以在我的會話中添加一些必要的變數,我將它們添加到Application_PostAuthenticateRequest事件處理程序中的自定義 IIdentity 中。但是,我Context.Sessionnull存在,所以這也不是要走的路。

我已經為此工作了一天,我覺得我錯過了一些東西。這應該不難做到,對吧?我也對隨之而來的所有(半)相關內容感到有些困惑。MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication…. 我是唯一一個覺得這一切都非常混亂的人嗎?

如果有人能告訴我一個簡單、優雅、高效的解決方案,可以在 IIdentity 上儲存一些額外的數據,而不需要額外的模糊……那就太好了!我知道關於 SO 有類似的問題,但如果我需要的答案在那裡,我一定忽略了。

這是我的做法。

我決定使用 IPrincipal 而不是 IIdentity,因為這意味著我不必同時實現 IIdentity 和 IPrincipal。

  1. 創建介面
interface ICustomPrincipal : IPrincipal
{
   int Id { get; set; }
   string FirstName { get; set; }
   string LastName { get; set; }
}
  1. 自定義主體
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; }
}
  1. CustomPrincipalSerializeModel - 用於將自定義資訊序列化到 FormsAuthenticationTicket 對像中的 userdata 欄位中。
public class CustomPrincipalSerializeModel
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
}
  1. 登錄方法 - 使用自定義資訊設置 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");
}
  1. 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;
   }
}
  1. 在 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

引用自:https://stackoverflow.com/questions/1064271