ASP.NET MVC - 设置自定义 IIdentity 或 IPrincipal

发布于 2024-07-25 18:16:26 字数 1084 浏览 5 评论 0原文

我需要做一些相当简单的事情:在我的 ASP.NET MVC 应用程序中,我想设置一个自定义 IIdentity / IPrincipal。 哪个更容易/更合适。 我想扩展默认值,以便可以调用诸如 User.Identity.IdUser.Identity.Role 之类的内容。 没什么特别的,只是一些额外的属性。

我读过大量的文章和问题,但我觉得我让事情变得比实际更难。 我以为这很容易。 如果用户登录,我想设置一个自定义 IIdentity。 所以我想,我将在我的 global.asax 中实现 Application_PostAuthenticateRequest 。 但是,这是在每个请求上调用的,并且我不想在每个请求上调用数据库,这会请求数据库中的所有数据并放入自定义 IPrincipal 对象。 这似乎也非常不必要,缓慢,并且在错误的地方(在那里进行数据库调用),但我可能是错的。 或者这些数据还来自哪里?

所以我想,每当用户登录时,我都可以在会话中添加一些必要的变量,并将其添加到 Application_PostAuthenticateRequest 事件处理程序中的自定义 IIdentity 中。 但是,我的 Context.Session 是 null ,所以这也不是可行的方法。

我已经为此工作了一天,我觉得我错过了一些东西。 这应该不难做到,对吧? 我也对随之而来的所有(半)相关的东西感到有点困惑。 MembershipProviderMembershipUserRoleProviderProfileProviderIPrincipalIIdentity< /code>, FormsAuthentication...我是唯一一个觉得这一切非常令人困惑的人吗?

如果有人可以告诉我一个简单、优雅且高效的解决方案来在 IIdentity 上存储一些额外的数据,而无需所有额外的模糊......那就太好了! 我知道也有类似的问题,但如果我需要的答案就在那里,我一定是忽略了。

I need to do something fairly simple: in my ASP.NET MVC application, I want to set a custom IIdentity / IPrincipal. Whichever is easier / more suitable. I want to extend the default so that I can call something like User.Identity.Id and User.Identity.Role. Nothing fancy, just some extra properties.

I've read tons of articles and questions but I feel like I'm making it harder than it actually is. I thought it would be easy. If a user logs on, I want to set a custom IIdentity. So I thought, I will implement Application_PostAuthenticateRequest in my global.asax. However, that is called on every request, and I don't want to do a call to the database on every request which would request all the data from the database and put in a custom IPrincipal object. That also seems very unnecessary, slow, and in the wrong place (doing database calls there) but I could be wrong. Or where else would that data come from?

So I thought, whenever a user logs in, I can add some necessary variables in my session, which I add to the custom IIdentity in the Application_PostAuthenticateRequest event handler. However, my Context.Session is null there, so that is also not the way to go.

I've been working on this for a day now and I feel I'm missing something. This shouldn't be too hard to do, right? I'm also a bit confused by all the (semi)related stuff that comes with this. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication.... Am I the only one who finds all this very confusing?

If someone could tell me a simple, elegant, and efficient solution to store some extra data on a IIdentity without all the extra fuzz.. that would be great! I know there are similar questions on SO but if the answer I need is in there, I must've overlooked.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

手长情犹 2024-08-01 18:16:27

我不能直接谈论 ASP.NET MVC,但对于 ASP.NET Web 表单,技巧是创建一个 FormsAuthenticationTicket 并在用户通过身份验证后将其加密到 cookie 中。 这样,您只需调用数据库一次(或 AD 或您用于执行身份验证的任何内容),并且每个后续请求都将根据存储在 cookie 中的票证进行身份验证。

关于此的一篇好文章:http:// www.ondotnet.com/pub/a/dotnet/2004/02/02/ effectiveformsauth.html(损坏的链接)

编辑:

由于上面的链接已损坏,我会在上面的回答中推荐 LukeP 的解决方案: https://stackoverflow.com/a/10524305 - 我还建议接受答案改为那个。

编辑2:
损坏链接的替代方法: https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/ effectiveformsauth.html

I can't speak directly for ASP.NET MVC, but for ASP.NET Web Forms, the trick is to create a FormsAuthenticationTicket and encrypt it into a cookie once the user has been authenticated. This way, you only have to call the database once (or AD or whatever you are using to perform your authentication), and each subsequent request will authenticate based on the ticket stored in the cookie.

A good article on this: http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html (broken link)

Edit:

Since the link above is broken, I would recommend LukeP's solution in his answer above: https://stackoverflow.com/a/10524305 - I would also suggest that the accepted answer be changed to that one.

Edit 2:
An alternative for the broken link: https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html

给我一枪 2024-08-01 18:16:27

这是完成工作的示例。 bool isValid 通过查看某些数据存储(假设您的用户数据库)来设置。 UserID 只是我维护的一个 ID。 您可以将电子邮件地址等其他信息添加到用户数据中。

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

这将允许您访问自定义用户信息。

Here is an example to get the job done. bool isValid is set by looking at some data store (lets say your user data base). UserID is just an ID i am maintaining. You can add aditional information like email address to user data.

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);
     }
}

in the golbal asax add the following code to retrive your information

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;
    }
}

When you are going to use the information later, you can access your custom principal as follows.

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

this will allow you to access custom user information.

梦里泪两行 2024-08-01 18:16:27

MVC 为您提供了挂在控制器类上的 OnAuthorize 方法。 或者,您可以使用自定义操作过滤器来执行授权。 MVC 使它变得非常容易做到。 我在这里发布了一篇关于此的博客文章。 http://www.bradygaster.com/post/custom-authentication-with -mvc-3.0

MVC provides you with the OnAuthorize method that hangs from your controller classes. Or, you could use a custom action filter to perform authorization. MVC makes it pretty easy to do. I posted a blog post about this here. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0

梦里泪两行 2024-08-01 18:16:27

如果您需要将某些方法连接到 @User 以在视图中使用,这里有一个解决方案。 没有任何严肃的会员定制的解决方案,但如果原始问题仅需要视图,那么这可能就足够了。 下面的代码用于检查从authorizefilter返回的变量,用于验证是否存在某些链接(不适用于任何类型的授权逻辑或访问授予)。

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()

Here is a solution if you need to hook up some methods to @User for use in your views. No solution for any serious membership customization, but if the original question was needed for views alone then this perhaps would be enough. The below was used for checking a variable returned from a authorizefilter, used to verify if some links wehere to be presented or 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
            }
        }
    }

Then just add a reference in the areas web.config, and call it like below in the view.

@User.IsEditor()
情愿 2024-08-01 18:16:27

基于LukeP的回答,并添加一些方法来设置timeoutrequireSSL 与 Web.config 配合。

参考链接

LukeP修改代码

1、根据Web设置timeout。配置FormsAuthentication.Timeout< /a> 将获取超时值,该值在 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;

Based on LukeP's answer, and add some methods to setup timeout and requireSSL cooperated with Web.config.

The references links

Modified Codes of LukeP

1, Set timeout based on Web.Config. The FormsAuthentication.Timeout will get the timeout value, which is defined in web.config. I wrapped the followings to be a function, which return a ticket back.

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, Configure the cookie to be secure or not, based on the RequireSSL configuration.

HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;
时间你老了 2024-08-01 18:16:27

如果您想简化页面隐藏代码中的访问,只需将以下代码添加到基页面并在所有页面中派生基页面:

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

作为 Web 表单用户(而非 MVC)的 LukeP 代码的补充, 您后面的代码可以简单地访问:

User.FirstName or User.LastName

我在 Web 表单场景中缺少的是如何在未绑定到页面的代码中获得相同的行为,例如在 httpmodules 中,我应该始终添加在每个班级中演员,还是有更聪明的方法来获得这个?

感谢您的回答并感谢 LukeP,因为我使用您的示例作为我的自定义用户的基础(现在有 User.RolesUser.TasksUser .HasPath(int)User.Settings.Timeout 和许多其他好东西)

As an addition to LukeP code for Web Forms users (not MVC) if you want to simplify the access in the code behind of your pages, just add the code below to a base page and derive the base page in all your pages:

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

So in your code behind you can simply access:

User.FirstName or User.LastName

What I'm missing in a Web Form scenario, is how to obtain the same behaviour in code not tied to the page, for example in httpmodules should I always add a cast in each class or is there a smarter way to obtain this?

Thanks for your answers and thank to LukeP since I used your examples as a base for my custom user (which now has User.Roles, User.Tasks, User.HasPath(int) , User.Settings.Timeout and many other nice things)

盗琴音 2024-08-01 18:16:27

好吧,所以我是一个严肃的密码管理员,通过拖出这个非常古老的问题,但是有一个更简单的方法来解决这个问题,上面的@Baserz 已经提到了。 那就是使用 C# 扩展方法和缓存的组合(不要使用会话)。

事实上,微软已经在 Microsoft.AspNet.Identity.IdentityExtensions 命名空间。 例如,GetUserId() 是一个返回用户 ID 的扩展方法。 还有 GetUserName()FindFirstValue(),它们返回基于 IPrincipal 的声明。

因此,您只需包含命名空间,然后调用 User.Identity.GetUserName() 即可获取 ASP.NET Identity 配置的用户名。

我不确定这是否被缓存,因为旧的 ASP.NET Identity 不是开源的,而且我也懒得对其进行逆向工程。 但是,如果不是,那么您可以编写自己的扩展方法,该方法会将此结果缓存特定的时间。

All right, so i'm a serious cryptkeeper here by dragging up this very old question, but there is a much simpler approach to this, which was touched on by @Baserz above. And that is to use a combination of C# Extension methods and caching (Do NOT use session).

In fact, Microsoft has already provided a number of such extensions in the Microsoft.AspNet.Identity.IdentityExtensions namespace. For instance, GetUserId() is an extension method that returns the user Id. There is also GetUserName() and FindFirstValue(), which returns claims based on the IPrincipal.

So you need only include the namespace, and then call User.Identity.GetUserName() to get the users name as configured by ASP.NET Identity.

I'm not certain if this is cached, since the older ASP.NET Identity is not open sourced, and I haven't bothered to reverse engineer it. However, if it's not then you can write your own extension method, that will cache this result for a specific amount of time.

匿名。 2024-08-01 18:16:27

我尝试了LukeP建议的解决方案,发现它不支持Authorize属性。 所以,我稍微修改了一下。

public class UserExBusinessInfo
{
    public int BusinessID { get; set; }
    public string Name { get; set; }
}

public class UserExInfo
{
    public IEnumerable<UserExBusinessInfo> BusinessInfo { get; set; }
    public int? CurrentBusinessID { get; set; }
}

public class PrincipalEx : ClaimsPrincipal
{
    private readonly UserExInfo userExInfo;
    public UserExInfo UserExInfo => userExInfo;

    public PrincipalEx(IPrincipal baseModel, UserExInfo userExInfo)
        : base(baseModel)
    {
        this.userExInfo = userExInfo;
    }
}

public class PrincipalExSerializeModel
{
    public UserExInfo UserExInfo { get; set; }
}

public static class IPrincipalHelpers
{
    public static UserExInfo ExInfo(this IPrincipal @this) => (@this as PrincipalEx)?.UserExInfo;
}


    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginModel details, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            AppUser user = await UserManager.FindAsync(details.Name, details.Password);

            if (user == null)
            {
                ModelState.AddModelError("", "Invalid name or password.");
            }
            else
            {
                ClaimsIdentity ident = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
                AuthManager.SignOut();
                AuthManager.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);

                user.LastLoginDate = DateTime.UtcNow;
                await UserManager.UpdateAsync(user);

                PrincipalExSerializeModel serializeModel = new PrincipalExSerializeModel();
                serializeModel.UserExInfo = new UserExInfo()
                {
                    BusinessInfo = await
                        db.Businesses
                        .Where(b => user.Id.Equals(b.AspNetUserID))
                        .Select(b => new UserExBusinessInfo { BusinessID = b.BusinessID, Name = b.Name })
                        .ToListAsync()
                };

                JavaScriptSerializer serializer = new JavaScriptSerializer();

                string userData = serializer.Serialize(serializeModel);

                FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                         1,
                         details.Name,
                         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 RedirectToLocal(returnUrl);
            }
        }
        return View(details);
    }

最后在 Global.asax.cs

    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();
            PrincipalExSerializeModel serializeModel = serializer.Deserialize<PrincipalExSerializeModel>(authTicket.UserData);
            PrincipalEx newUser = new PrincipalEx(HttpContext.Current.User, serializeModel.UserExInfo);
            HttpContext.Current.User = newUser;
        }
    }

现在我可以通过调用来访问视图和控制器中的数据要注销

User.ExInfo()

我只需调用

AuthManager.SignOut();

AuthManager 所在的位置

HttpContext.GetOwinContext().Authentication

I tried the solution suggested by LukeP and found that it doesn't support the Authorize attribute. So, I modified it a bit.

public class UserExBusinessInfo
{
    public int BusinessID { get; set; }
    public string Name { get; set; }
}

public class UserExInfo
{
    public IEnumerable<UserExBusinessInfo> BusinessInfo { get; set; }
    public int? CurrentBusinessID { get; set; }
}

public class PrincipalEx : ClaimsPrincipal
{
    private readonly UserExInfo userExInfo;
    public UserExInfo UserExInfo => userExInfo;

    public PrincipalEx(IPrincipal baseModel, UserExInfo userExInfo)
        : base(baseModel)
    {
        this.userExInfo = userExInfo;
    }
}

public class PrincipalExSerializeModel
{
    public UserExInfo UserExInfo { get; set; }
}

public static class IPrincipalHelpers
{
    public static UserExInfo ExInfo(this IPrincipal @this) => (@this as PrincipalEx)?.UserExInfo;
}


    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginModel details, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            AppUser user = await UserManager.FindAsync(details.Name, details.Password);

            if (user == null)
            {
                ModelState.AddModelError("", "Invalid name or password.");
            }
            else
            {
                ClaimsIdentity ident = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
                AuthManager.SignOut();
                AuthManager.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);

                user.LastLoginDate = DateTime.UtcNow;
                await UserManager.UpdateAsync(user);

                PrincipalExSerializeModel serializeModel = new PrincipalExSerializeModel();
                serializeModel.UserExInfo = new UserExInfo()
                {
                    BusinessInfo = await
                        db.Businesses
                        .Where(b => user.Id.Equals(b.AspNetUserID))
                        .Select(b => new UserExBusinessInfo { BusinessID = b.BusinessID, Name = b.Name })
                        .ToListAsync()
                };

                JavaScriptSerializer serializer = new JavaScriptSerializer();

                string userData = serializer.Serialize(serializeModel);

                FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                         1,
                         details.Name,
                         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 RedirectToLocal(returnUrl);
            }
        }
        return View(details);
    }

And finally in Global.asax.cs

    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();
            PrincipalExSerializeModel serializeModel = serializer.Deserialize<PrincipalExSerializeModel>(authTicket.UserData);
            PrincipalEx newUser = new PrincipalEx(HttpContext.Current.User, serializeModel.UserExInfo);
            HttpContext.Current.User = newUser;
        }
    }

Now I can access the data in views and controllers simply by calling

User.ExInfo()

To log out I just call

AuthManager.SignOut();

where AuthManager is

HttpContext.GetOwinContext().Authentication
﹏雨一样淡蓝的深情 2024-08-01 18:16:26

我是这样做的。

我决定使用 IPrincipal 而不是 IIdentity,因为这意味着我不必同时实现 IIdentity 和 IPrincipal。

  1. 创建界面

    接口 ICustomPrincipal : IPrincipal 
      { 
          int Id { 得到;   放;   } 
          字符串名字{获取;   放;   } 
          字符串姓氏{获取;   放;   } 
      } 
      
  2. CustomPrincipal

    公共类 CustomPrincipal : ICustomPrincipal 
      { 
          公共IIdentity身份{获取;   私人套装;   } 
          公共 bool IsInRole(字符串角色) { return false;   } 
    
          公共 CustomPrincipal(字符串电子邮件) 
          { 
              this.Identity = new GenericIdentity(电子邮件); 
          } 
    
          公共 int Id { 得到;   放;   } 
          公共字符串名字{获取;   放;   } 
          公共字符串姓氏{获取;   放;   } 
      } 
      
  3. CustomPrincipalSerializeModel - 用于将自定义信息序列化到 FormsAuthenticationTicket 对象中的 userdata 字段中。

    公共类 CustomPrincipalSerializeModel 
      { 
          公共 int Id { 得到;   放;   } 
          公共字符串名字{获取;   放;   } 
          公共字符串姓氏{获取;   放;   } 
      } 
      
  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 = 用户.Id; 
          SerializeModel.FirstName = user.FirstName; 
          SerializeModel.LastName = user.LastName; 
    
          JavaScriptSerializer 序列化器 = new JavaScriptSerializer(); 
    
          字符串 userData = serializer.Serialize(serializeModel); 
    
          FormsAuthenticationTicket authTicket = 新 FormsAuthenticationTicket( 
                   1、 
                   viewModel.电子邮件, 
                   日期时间.现在, 
                   日期时间.Now.AddMinutes(15), 
                   错误的, 
                   用户数据); 
    
          字符串 encTicket = FormsAuthentication.Encrypt(authTicket); 
          HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); 
          Response.Cookies.Add(faCookie); 
    
          返回 RedirectToAction("索引", "主页"); 
      } 
      
  5. Global.asax.cs - 读取 cookie 并替换 HttpContext.User 对象,这是通过重写 PostAuthenticateRequest 来完成的

    protected void Application_PostAuthenticateRequest(对象发送者, EventArgs e) 
      { 
          HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName]; 
    
          if (authCookie != null) 
          { 
              FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
    
              JavaScriptSerializer 序列化器 = new JavaScriptSerializer(); 
    
              CustomPrincipalSerializeModel serializeModel = serializer.Deserialize(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 视图中访问

    来 完成的

    @((用户作为 CustomPrincipal).Id) 
      @((用户作为 CustomPrincipal).FirstName) 
      @((用户作为 CustomPrincipal).LastName) 
      

和代码中访问:

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

我认为代码是不言自明的。 如果不是,请告诉我。

此外,为了使访问更加容易,您可以创建一个基本控制器并覆盖返回的 User 对象(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

Here's how I do it.

I decided to use IPrincipal instead of IIdentity because it means I don't have to implement both IIdentity and IPrincipal.

  1. Create the interface

    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 - for serializing custom information into userdata field in FormsAuthenticationTicket object.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
  4. LogIn method - setting up a cookie with custom information

    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 - Reading cookie and replacing HttpContext.User object, this is done by overriding 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. Access in Razor views

    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)
    

and in code:

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

I think the code is self-explanatory. If it isn't, let me know.

Additionally to make the access even easier you can create a base controller and override the returned User object (HttpContext.User):

public class BaseController : Controller
{
    protected virtual new CustomPrincipal User
    {
        get { return HttpContext.User as CustomPrincipal; }
    }
}

and then, for each controller:

public class AccountController : BaseController
{
    // ...
}

which will allow you to access custom fields in code like this:

User.Id
User.FirstName
User.LastName

But this will not work inside views. For that you would need to create a custom WebViewPage implementation:

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; }
    }
}

Make it a default page type in 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>

and in views, you can access it like this:

@User.FirstName
@User.LastName
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文