asp.net - 快速修改問答信息 ASP.NET MVC - - 設置自定義IIdentity或者IPrincipal

  显示原文与译文双语对照的内容

我需要做一些非常簡單的事情: 在我的ASP.NET MVC應用程序中,我想設置一個定製的iidentity/rr 。 哪個更容易/更合適。我想擴展默認值,這樣我可以調用像 User.Identity.IdUser.Identity.Role 這樣的東西。 沒什麼新奇的,只是一些額外的屬性。

我已經閱讀了大量的文章和問題,但我覺得我比實際更難。 我覺得這很容易。 如果一個用戶登錄,我想設置一個自定義iidentity。 所以我想,我將實現 Application_PostAuthenticateRequest 但是,在我的global.asax. 中,每個請求都調用了這個調用,我不想對每個請求調用資料庫,請求來自資料庫的所有數據並放入自定義IPrincipal對象。 這似乎是不必要的,慢,而且在錯誤的地方,( 正在進行資料庫調用) 但我可以錯。 或者其他數據來自哪裡?

所以我想,每當用戶登錄時,我都可以在我的會話中添加一些必要的變數,我將這些變數添加到 Application_PostAuthenticateRequest 事件處理程序。但是,我的Context.Sessionnull,所以這也不是。

我在這一天工作了一天,我覺得我缺少了一些東西。 這應該不難做到,對吧? 我也有點迷惑,所有的( 半) 相關的東西,伴隨著。 MembershipProviderMembershipUserRoleProviderProfileProviderIPrincipalIIdentityFormsAuthentication 。。 我是唯一一個發現這一切非常混亂的人?

如果有人能告訴我一個簡單的,優雅的,高效的解決方案來存儲一些額外的數據在一個 IIdentity,沒有多餘的模糊。 那太好了我知道有類似的問題,但是如果我需要的答案是在那裡,我必須忽略。 謝謝。

时间:

我就是這麼做的。

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

  1. 創建介面

    
    interface ICustomPrincipal : IPrincipal
    {
     int Id { get; set; }
     string FirstName { get; set; }
     string LastName { get; set; }
    }
    
    
  2. CustomPrincipal

    
    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; }
    }
    
    
  3. CustomPrincipalSerializeModel - 用於將自定義信息序列化到FormsAuthenticationTicket對象中的userdata欄位。

    
    public class CustomPrincipalSerializeModel
    {
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
    }
    
    
  4. 登錄方法- 使用自定義信息設置 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");
    }
    
    
  5. 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;
     }
    }
    
    
  6. 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

我認為代碼是 self-explanatory 。 如果不是,讓我知道。

另外,為了使訪問更容易,你可以創建一個基本控制器並覆蓋返回的用戶對象( 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; }
 }
}

將它的作為視圖/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

HTH

我不能直接為 ASP.NET MVC說話,但是對於 ASP.NET Web表單,竅門是創建一個 FormsAuthenticationTicket 並在用戶經過身份驗證后將它的加密到cookie中。 這樣,你只需要一次調用資料庫一次( 或者廣告或者任何你用來執行認證的東西),隨後的每個請求都將基於存儲在cookie中的票證進行身份驗證。

關於這個的一篇好文章: http://www.ondotnet.com/pub dotnet/1 2004/02/02 1/effectiveformsauth. html ( 斷開的鏈接)

編輯:

由於上面的鏈接已損壞,我建議在下面的答案中解決lukep問題: http://stackoverflow.com 10524305 web - 我也建議將接受的答案更改為那個。

下面是一個完成任務的例子。 通過查看某些數據存儲( 假設你的用戶資料庫) 設置 bool isValid 。 UserID只是我維護的一個標識。 你可以向用戶數據添加aditional信息如電子郵件地址。


protected void btnLogin_Click(object sender, EventArgs e)
{ 
//Hard Coded for the moment
 bool isValid=true;
 if (isValid) 
 {
 string userData = String.Empty;
 userData = userData +"UserID=" + userID;
 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
 string encTicket = FormsAuthentication.Encrypt(ticket);
 HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
 Response.Cookies.Add(faCookie);
//And send the user where they were heading
 string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
 Response.Redirect(redirectUrl);
 }
}

在 golbal asax中添加以下代碼檢索你的信息


protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
 HttpCookie authCookie = Request.Cookies[
 FormsAuthentication.FormsCookieName];
 if(authCookie!= null)
 {
//Extract the forms authentication cookie
 FormsAuthenticationTicket authTicket = 
 FormsAuthentication.Decrypt(authCookie.Value);
//Create an Identity object
//CustomIdentity implements System.Web.Security.IIdentity
 CustomIdentity id = GetUserIdentity(authTicket.Name);
//CustomPrincipal implements System.Web.Security.IPrincipal
 CustomPrincipal newUser = new CustomPrincipal();
 Context.User = newUser;
 }
}

在以後使用這些信息時,你可以訪問自定義主體,如下所示。


(CustomPrincipal)this.User
or 
(CustomPrincipal)this.Context.User

這將允許你訪問自定義用戶信息。

MVC為你提供了從控制器類掛起的OnAuthorize方法。 或者,你可以使用自定義操作篩選器執行授權。 MVC使得它很容易做到。 我在這裡發表了一篇關於這個的博客文章。 http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

下面是一個解決方案,如果你需要將一些方法連接到 @User 以便在你的視圖中使用。 沒有任何解決方案可以用於任何嚴重的成員自定義,但是如果只需要原始問題的原始問題,那麼可能就足夠了。 下面用於檢查從authorizefilter返回的變數,用於驗證是否有wehere鏈接被顯示或者 not(not for any kind of authorization logic or access granting) 。


using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Web;
 using System.Security.Principal;

 namespace SomeSite.Web.Helpers
 {
 public static class UserHelpers
 {
 public static bool IsEditor(this IPrincipal user)
 {
 return null;//Do some stuff
 }
 }
 }

然後在區域 Web.Config 中添加一個引用,並在視圖中調用它。

 
@User.IsEditor()

 

基於 lukep的應答,並添加一些方法來安裝 timeoutrequireSSLWeb.config

引用鏈接

修改的LukeP

基於 Web.config 1,timeoutFormsAuthentication.Timeout 將獲取超時值,它在 Web.Config 中定義。 我將以下內容包裝為一個函數,返回一個 ticket


int version = 1;
DateTime now = DateTime.Now;

//respect to the `timeout` in Web.config.
TimeSpan timeout = FormsAuthentication.Timeout;
DateTime expire = now.Add(timeout);
bool isPersist = false;

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
 version, 
 name,
 now,
 expire,
 isPersist,
 userData);

2,根據 requireSSL 配置,將cookie配置為安全或者不安全。


HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
//respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;

作為web表單用戶除了LukeP代碼( 不是 MVC ) 如果你想簡化背後的代碼中訪問你的頁面,將下面的代碼添加到一個基地頁面和推導出基地在所有頁面:


Public Overridable Shadows ReadOnly Property User() As CustomPrincipal
 Get
 Return DirectCast(MyBase.User, CustomPrincipal)
 End Get
End Property

所以在你的代碼中,你可以簡單地訪問:


User.FirstName or User.LastName

我丟失在web表單的情況下,如何在代碼中獲得相同的行為不是綁定到頁面,例如在 httpmodules我應該總是在每個類添加一個演員或有聰明的方法來獲得呢?

感謝你的回答和感謝 LukeP,因為我使用了你的示例作為我自定義用戶的基礎( 現在有 User.RolesUser.TasksUser.HasPath(int)User.Settings.Timeout 和許多其他好東西)

你可以打開 ASP.NET mvc 4 web應用程序模板項目( 至少在vs2013中),並查看它是如何實現的。

我會試著用它。

...