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


130

我正在使用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; }
    }

如何从控制器内部检索当前登录用户的OrganizationId属性?用户登录后,是否可以通过方法使用该方法?或者每次执行控制器方法时,我总能基于UserId从数据库中检索OrganizationId吗?

在网上阅读后,我发现我需要使用以下内容来获取登录的UserId等。

using Microsoft.AspNet.Identity;
...
User.Identity.GetUserId();

但是,OrganizationId不是User.Identity中可用的属性。我是否需要扩展User.Identity以包括OrganizationId属性?如果是这样,我该如何处理。

我经常需要OrganizationId的原因是,许多表查询都依赖OrganizationId来检索与与登录用户相关联的Organization相关的数据。


3
难道我的答案在这里帮你呢?
Shoe 2015年

1
在这里,我的答案几乎相同:stackoverflow.com/a/28138594/809357-如果在请求有效期内定期需要此信息,则可以将其放在Cookie上作为索赔。
Trailmax 2015年

1
感谢@Shoe,您的答案都奏效。除了您的答案外,我还必须添加一个声明以存储在cookie中。在IdentityModels类中,我必须添加userIdentity.AddClaim(new Claim(“ MyApp:OrganizationId”,OrganizationId.ToString()));。公共异步Task <ClaimsIdentity> GenerateUserIdentityAsync(UserManager <ApplicationUser> manager) 方法。
RobHurd

Answers:


219

每当您想使用任何其他属性(如上述问题)扩展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; }
}

然后,您需要创建一个扩展方法,如下所示(我在新的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;
        }
    }
}

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

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

就我而言:using App.Extensions;在Controller内并@using. App.Extensions带有.cshtml View文件。

编辑:

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

<add namespace="App.Extensions" />

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

您可以访问扩展方法:

var orgId = User.Identity.GetOrganizationId();

嗨,帕维尔,我已经尝试过相同的方法,但是却收到错误消息,即应用程序用户未包含OrganisationId的定义
这是一个陷阱

@RachitGupta什么时候发生?当您尝试添加索赔时,或者当您尝试在代码中稍后访问索赔值时?如果是在添加声明,那么请确保您的ApplicationUser定义了属性...如果在代码的后面,请不要忘记将using语句添加到创建扩展方法的位置,例如:using App.Extensions ;
Pawel

在userIdentity.AddClaim行中出错。我仅在IdentityModels.cs文件中创建了IdentityExtensions类。这会成为问题的根源吗?
这是一个陷阱

是的,如果是在添加声明时,它是在identityExtensions起作用之前(也就是在您读回值时)...确保您的ApplicationUser拥有您要添加为声明的属性:公共长OrganizationId { 组; }
Pawel

6
谢谢,它有效。我认为这是Asp Net Idendity 2中自定义变量的最佳做法。我不知道为什么Asp.Net社区在其网站上的默认文章中未提供这样的示例。
oneNic​​eFriend

17

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

@using programname.Models.Extensions

我一直在寻找FirstName,以便在用户登录后显示在我的NavBar的右上方。

我以为我会在有帮助的情况下发布此消息,所以这是我的代码:

我创建了一个名为Extensions(在我的Models文件夹下)的新文件夹,并将新类创建为上面指定的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;
        }
    }
}

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

然后在我的_LoginPartial.cshtml(在Views/Shared文件夹下)添加@using.ProgramName.Models.Extensions

然后,我将更改添加到以下代码行中,该代码行将在登录后使用“用户名”:

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

也许这可以帮助其他人。


11

请查看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);
        }
    }

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

    // Add the new address properties:
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }

然后更新“注册视图”以包括新属性。

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

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

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;

16
这是一个很好的例子,但它没有回答问题,即如何从User.Identity获取那些新属性。
Dejan Bogatinovski 2015年

5
拒绝投票,是因为答案没有显示如何从User.Identity检索自定义属性。
maulik13 2013年

3

对于任何发现此问题并寻找如何在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>
}

2
应该注意的是,GetUserAsync(User)将查询数据库以检索OrganizationId。相反,可接受的解决方案将在索赔中包含OrganizationId(例如cookie)。从数据库中提取此信息的好处是,人们可以在组织之间移动,而无需注销/登录。当然,缺点是它需要附加的数据库查询。
马特

1

Dhaust提供了一种将属性添加到ApplicationUser类的好方法。查看OP代码,看来他们可能已经这样做了,或者已经按照计划进行了。问题问

如何从控制器内部检索当前登录用户的OrganizationId属性?但是,OrganizationId不是User.Identity中可用的属性。我是否需要扩展User.Identity以包括OrganizationId属性?

Pawel提供了一种添加扩展方法的方法,该方法需要使用语句或将名称空间添加到web.config文件。

但是,该问题询问您是否“需要”扩展User.Identity以包括新属性。没有扩展User.Identity的另一种方法来访问属性。如果遵循Dhaust方法,则可以在控制器中使用以下代码访问新属性。

ApplicationDbContext db = new ApplicationDbContext();
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db));
var currentUser = manager.FindById(User.Identity.GetUserId());
var myNewProperty = currentUser.OrganizationId;

0

我还向AspNetUsers表中添加或扩展了其他列。当我只想查看这些数据时,我发现了许多示例,例如上面带有“扩展名”的代码等。这让我感到非常惊奇,您必须编写所有这些代码行才能从当前用户那里获得几个价值。

事实证明,您可以像查询其他任何表一样查询AspNetUsers表:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.