Как расширить доступные свойства User.Identity

130

Я использую MVC5 Identity 2.0, чтобы пользователи могли входить на мой веб-сайт, где данные аутентификации хранятся в базе данных SQL. Asp.net Identity реализован стандартным образом, что можно найти во многих онлайн-руководствах.

Класс ApplicationUser в IdentityModels был расширен за счет включения некоторых настраиваемых свойств, таких как целое число 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 текущего вошедшего в систему пользователя из контроллера? Доступно ли это с помощью метода после входа пользователя в систему, или я всегда получаю OrganizationId из базы данных на основе UserId каждый раз, когда выполняется метод контроллера?

Читая в Интернете, я видел, что мне нужно использовать следующее, чтобы войти в UserId и т. Д.

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

Однако OrganizationId не является свойством, доступным в User.Identity. Нужно ли мне расширять User.Identity, чтобы включить свойство OrganizationId? Если да, то как мне это сделать?

Причина, по которой мне так часто нужен OrganizationId, заключается в том, что многие запросы к таблицам зависят от OrganizationId для получения данных, относящихся к организации, связанной с вошедшим в систему пользователем.

RobHurd
источник
3
Мой ответ здесь вам вообще помогает?
Shoe
1
Практически тот же ответ от меня здесь: stackoverflow.com/a/28138594/809357 - если вам нужна эта информация регулярно в течение жизни запроса, вы можете поместить ее в файл cookie в качестве требования.
trailmax
1
Спасибо @Shoe, оба ваших ответа сработали. В дополнение к вашим ответам мне пришлось добавить заявку, которая будет храниться в файле cookie. В классе IdentityModels мне пришлось добавить userIdentity.AddClaim (новое утверждение ("MyApp: OrganizationId", OrganizationId.ToString ())); к общедоступному асинхронному методу Task <ClaimsIdentity> GenerateUserIdentityAsync (UserManager <ApplicationUser> manager) .
RobHurd,

Ответы:

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

Когда вы создаете Identity в классе ApplicationUser, просто добавьте 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;внутри контроллера и @using. App.Extensionsв файле просмотра .cshtml.

РЕДАКТИРОВАТЬ:

Что вы также можете сделать, чтобы избежать добавления оператора using в каждый View, так это перейти в папку Views и найти там файл Web.config. Теперь найдите <namespaces>тег и добавьте туда свое пространство имен расширения, например:

<add namespace="App.Extensions" />

Сохраните файл, и все готово. Теперь каждое представление будет знать о ваших расширениях.

Вы можете получить доступ к методу расширения:

var orgId = User.Identity.GetOrganizationId();
Pawel
источник
Привет, Павел, я пробовал то же самое, но получаю сообщение об ошибке, что пользователь приложения не содержит определения OrganisationId
это ловушка
@RachitGupta Когда это произойдет? Когда вы пытаетесь добавить утверждение или когда пытаетесь получить доступ к его значению позже в коде? Если это происходит при добавлении утверждения, убедитесь, что в вашем ApplicationUser определено свойство ... Если оно находится позже в коде, не забудьте добавить оператор using туда, где вы создали метод расширения, например: using App.Extensions ;
Павел
Получена ошибка в строке userIdentity.AddClaim. Я создал класс IdentityExtensions только в файле IdentityModels.cs. Может ли это быть источником проблемы?
Это ловушка
Нет, если это когда вы добавляете утверждение, это происходит задолго до того, как identityExtensions вступает в игру (когда вы читаете значение обратно) ... Убедитесь, что ваш ApplicationUser имеет свойство, которое вы пытаетесь добавить в качестве утверждения: в этом примере это было public long OrganizationId {получить; устанавливать; }
Павел
6
Спасибо, сработало. Я думаю, что это лучшая практика для пользовательских переменных в Asp Net Idendity 2. Я не знаю, почему сообщество Asp.Net не предоставляет такой пример в статьях по умолчанию на своих сайтах.
oneNiceFriend 05
17

Я искал такое же решение, и Павел дал мне 99% ответа. Единственное, чего мне не хватало для отображения расширения, так это добавления следующего кода Razor на страницу cshtml (просмотр):

@using programname.Models.Extensions

Я искал имя, которое будет отображаться в правом верхнем углу панели навигации после входа пользователя в систему.

Я думал, что опубликую это, если это поможет кому-то другому, Итак, вот мой код:

Я создал новую папку под названием 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;
        }
    }
}

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

Возможно, это поможет кому-то еще в будущем.

AxleWack
источник
11

Ознакомьтесь с этим замечательным сообщением в блоге Джона Аттена: 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);
        }
    }

Затем вы добавляете свои новые свойства в свой RegisterViewModel.

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

Затем обновите метод Register () в AccountController новыми свойствами.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;
Dhaust
источник
16
Это хороший пример, но он не отвечает на вопрос, как получить эти новые свойства из User.Identity.
Деян Богатиновски
5
Проголосовали против, поскольку ответ не показывает, как можно получить настраиваемые свойства из User.Identity.
maulik13
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>
}
Jashan
источник
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;
codeMethod
источник
1

Я также добавил или расширил дополнительные столбцы в свою таблицу AspNetUsers. Когда я хотел просто просмотреть эти данные, я нашел много примеров, таких как приведенный выше код с «Расширениями» и т. Д. Меня действительно поразило то, что вам пришлось написать все эти строки кода только для того, чтобы получить пару значений от текущих пользователей.

Оказывается, вы можете запросить таблицу AspNetUsers, как любую другую таблицу:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
Энтони Григгс
источник