如何扩展User.Identity的可用属性

Rob*_*urd 125 asp.net-mvc asp.net-mvc-5 asp.net-identity asp.net-identity-2

我正在使用MVC5 Identity 2.0登录我的网站,其中身份验证详细信息存储在SQL数据库中.Asp.net Identity已经以标准方式实现,可以在许多在线教程中找到.

IdentityModels中的ApplicationUser类已扩展为包含一些自定义属性,例如整数OrganizationId.我们的想法是,可以创建许多用户并将其分配给公共组织以实现数据库关系.

public class ApplicationUser : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        //Extended Properties
        public DateTime? BirthDate { get; set; }
        public long? OrganizationId { get; set; }

        //Key Mappings
        [ForeignKey("OrganizationId")]
        public virtual Organization Organization { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

如何从控制器中检索当前登录用户的OrganizationId属性?一旦用户登录,这是否可通过方法获得,或者每次执行控制器方法时,我是否始终根据UserId从数据库中检索OrganizationId?

在网上阅读我看到我需要使用以下内容来登录UserId等.

using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();
Run Code Online (Sandbox Code Playgroud)

但是,OrganizationId不是User.Identity中可用的属性.我是否需要扩展User.Identity以包含OrganizationId属性?如果是这样,我该怎么做呢.

我经常需要OrganizationId的原因是许多表查询依赖于OrganizationId来检索与登录用户相关联的与组织相关的数据.

小智 213

每当您想要使用上述问题等任何其他属性扩展User.Identity的属性时,首先将这些属性添加到ApplicationUser类,如下所示:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    // Your Extended Properties
    public long? OrganizationId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

那么你需要的是创建一个像这样的扩展方法(我在一个新的Extensions文件夹中创建我的):

namespace App.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetOrganizationId(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("OrganizationId");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在ApplicationUser类中创建Identity时,只需添加Claim - > OrganizationId,如下所示:

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here => this.OrganizationId is a value stored in database against the user
        userIdentity.AddClaim(new Claim("OrganizationId", this.OrganizationId.ToString()));

        return userIdentity;
    }
Run Code Online (Sandbox Code Playgroud)

添加声明并使用扩展方法后,要使其作为User.Identity的属性可用,请在要访问它的页面/文件上添加using语句:

在我的情况下:using App.Extensions;在一个控制器内并@using. App.Extensions使用.cshtml查看文件.

编辑:

您还可以做的是避免在每个View中添加using语句,转到Views文件夹,然后在其中找到Web.config文件.现在查找<namespaces>标记并在其中添加扩展名称空间,如下所示:

<add namespace="App.Extensions" />
Run Code Online (Sandbox Code Playgroud)

保存文件,你就完成了.现在,每个View都会知道您的扩展程序.

您可以访问扩展方法:

var orgId = User.Identity.GetOrganizationId();
Run Code Online (Sandbox Code Playgroud)

希望能帮到任何人:)

  • 谢谢,它有效.我认为这是Asp Net Idendity 2中自定义变量的最佳实践.我不知道为什么Asp.Net社区不在其网站上的默认文章中提供这样的示例. (6认同)

Axl*_*ack 16

我正在寻找相同的解决方案,Pawel给了我99%的答案.我需要扩展显示的唯一缺少的是将以下Razor代码添加到cshtml(视图)页面:

@using programname.Models.Extensions

我正在寻找FirstName,在用户登录后显示在我的NavBar的右上角.

我以为我会发布这个帮助其他人,所以这是我的代码:

我创建了一个名为Extensions的新文件夹(在我的模型文件夹下)并创建了上面指定的Pawel新类: IdentityExtensions.cs

using System.Security.Claims;
using System.Security.Principal;

namespace ProgramName.Models.Extensions
{
    public static class IdentityExtensions
    {
        public static string GetUserFirstname(this IIdentity identity)
        {
            var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
            // Test for null to avoid issues during local testing
            return (claim != null) ? claim.Value : string.Empty;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

IdentityModels.cs :

public class ApplicationUser : IdentityUser
{

    //Extended Properties
    public string FirstName { get; internal set; }
    public string Surname { get; internal set; }
    public bool isAuthorized { get; set; }
    public bool isActive { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        userIdentity.AddClaim(new Claim("FirstName", this.FirstName));

        return userIdentity;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的_LoginPartial.cshtml(Under Views/SharedFolders)中我添加了@using.ProgramName.Models.Extensions

然后,我将更改添加到登录后将使用"用户优先"名称的代码行:

@Html.ActionLink("Hello " + User.Identity.GetUserFirstname() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })

也许这有助于其他人下线.


Dha*_*ust 10

查看John Atten撰写的这篇精彩博文: ASP.NET Identity 2.0:自定义用户和角色

它在整个过程中提供了很好的分步信息.去读它:)

以下是一些基础知识.

通过添加新属性(即地址,城市,州等)来扩展默认的ApplicationUser类:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> 
    GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this,  DefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

    // Use a sensible display name for views:
    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    // Concatenate the address info for display in tables and such:
    public string DisplayAddress
    {
        get
        {
            string dspAddress = string.IsNullOrWhiteSpace(this.Address) ? "" : this.Address;
            string dspCity = string.IsNullOrWhiteSpace(this.City) ? "" : this.City;
            string dspState = string.IsNullOrWhiteSpace(this.State) ? "" : this.State;
            string dspPostalCode = string.IsNullOrWhiteSpace(this.PostalCode) ? "" : this.PostalCode;

            return string.Format("{0} {1} {2} {3}", dspAddress, dspCity, dspState, dspPostalCode);
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后将新属性添加到R​​egisterViewModel.

    // Add the new address properties:
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后更新"注册视图"以包含新属性.

    <div class="form-group">
        @Html.LabelFor(m => m.Address, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Address, new { @class = "form-control" })
        </div>
    </div>
Run Code Online (Sandbox Code Playgroud)

然后使用新属性更新AccountController上的Register()方法.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的例子,但它没有回答这个问题,即如何从User.Identity获取这些新属性. (15认同)
  • Downvoted,因为答案没有显示如何从User.Identity中检索自定义属性. (5认同)

Jas*_*han 5

对于在 ASP.NET Core 2.1 中查找如何访问自定义属性的任何发现此问题的人 - 这要容易得多:您将拥有一个 UserManager,例如在 _LoginPartial.cshtml 中,然后您可以简单地执行(假设“ScreenName”是一个您已添加到您自己的 AppUser 的属性,该属性继承自 IdentityUser):

@using Microsoft.AspNetCore.Identity

@using <namespaceWhereYouHaveYourAppUser>

@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

@if (SignInManager.IsSignedIn(User)) {
    <form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Action("Index", "Home", new { area = "" })" 
          method="post" id="logoutForm" 
          class="form-inline my-2 my-lg-0">

        <ul class="nav navbar-nav ml-auto">
            <li class="nav-item">
                <a class="nav-link" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">
                    Hello @((await UserManager.GetUserAsync(User)).ScreenName)!
                    <!-- Original code, shows Email-Address: @UserManager.GetUserName(User)! -->
                </a>
            </li>
            <li class="nav-item">
                <button type="submit" class="btn btn-link nav-item navbar-link nav-link">Logout</button>
            </li>
        </ul>

    </form>
} else {
    <ul class="navbar-nav ml-auto">
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Register">Register</a></li>
        <li class="nav-item"><a class="nav-link" asp-area="Identity" asp-page="/Account/Login">Login</a></li>
    </ul>
}
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的是,`GetUserAsync(User)` 将查询数据库以检索 OrganizationId。相反,已接受的解决方案将在声明(例如 cookie)中包含 OrganizationId。从数据库中提取此信息的优点是人员可以在组织之间移动,而无需他们注销/登录。当然,缺点是需要额外的数据库查询。 (2认同)