Як розширити доступні властивості User.Identity


130

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

Клас ApplicationUser в IdentityModels був розширений, щоб включити деякі власні властивості, такі як ціла OrganiId. Ідея полягає в тому, що багато користувачів можуть бути створені та призначені до спільної Організації для цілей взаємозв'язку БД

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

Як я можу отримати властивість OrganId поточно зареєстрованого користувача у контролера? Чи це доступно через метод, коли користувач увійшов у систему чи я завжди маю отримати OrganiId з бази даних на основі UserId кожного разу, коли виконується метод контролера?

Читаючи в Інтернеті, я побачив, що для входу в UserId тощо потрібно використовувати наступне.

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

Однак OrganiId не є власністю, доступною в User.Identity. Чи потрібно мені розширити User.Identity, щоб включити властивість OrganId? Якщо так, то як мені це зробити.

Причина, з якою мені так часто потрібен OrganId, полягає в тому, що багато запитів таблиць покладаються на OrganiId для отримання даних, що стосуються Організації, пов'язаних із зареєстрованим користувачем.


3
Чи допомагає моя відповідь тут взагалі?
взуття

1
Приблизно однакова відповідь від мене тут: stackoverflow.com/a/28138594/809357 - якщо ця інформація вам потрібна регулярно в процесі запиту, ви можете розмістити її на файлі cookie як претензію.
trailmax

1
Дякую @Shoe, і ваша відповідь спрацювала. Окрім ваших відповідей, я мав додати претензію, яку потрібно зберігати у файлі cookie. У клас IdentityModels мені довелося додати userIdentity.AddClaim (нова претензія ("MyApp: OrganId", OrganId.ToString ())); до загальнодоступного методу Завдання асинхронізації <ClaimsIdentity> GenerateUserIdentityAsync (UserManager <ApplicationUser> менеджер) .
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; }
}

Тоді вам потрібно створити такий спосіб розширення (я створюю мою в новій папці розширень):

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, просто додайте претензію -> OrganId так:

    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 App.Extensions;в Контролері та за @using. App.Extensionsдопомогою .cshtml Переглянути файл.

Редагувати:

Те, що ви також можете зробити, щоб уникнути додавання використовуючого оператора у кожен перегляд, - це перейти до папки Views і знайти там файл Web.config. Тепер шукайте <namespaces>тег і додайте туди простір імен розширень так:

<add namespace="App.Extensions" />

Збережіть файл і все закінчено. Тепер кожен Перегляд буде знати про ваші розширення.

Ви можете отримати доступ до методу розширення:

var orgId = User.Identity.GetOrganizationId();

Привіт Pawel, я спробував те ж саме, але отримую помилку, що користувач програми не містить визначення OrganisationId
Це пастка

@RachitGupta Коли це відбувається? Коли ви намагаєтесь додати претензію чи намагаєтесь отримати пізніше в коді її значення? Якщо ви додаєте претензію, то переконайтеся, що у вашому ApplicationUser визначено властивість ... Якщо це пізніше в коді, тоді не забудьте додати оператор use до місця, де ви створили метод розширення, наприклад: за допомогою App.Extensions ;
Pawel

Отримала помилку в рядку userIdentity.AddClaim. Я створив клас IdentityExtensions лише у файлі IdentityModels.cs. Чи може це бути джерелом проблеми?
Це пастка

Ні, якщо ви додаєте претензію, це відбувається так, перш ніж ідентифікаційні розширення вступають у гру (саме тоді, коли ви читаєте значення назад) ... Переконайтеся, що ваш ApplicationUser має властивість, яку ви намагаєтеся додати як претензію: у цьому прикладі це було громадська довга OrganizationId {get; набір; }
Pawel

6
Спасибі, спрацювало. Я думаю, що це найкраща практика для користувацьких змінних у Asp Net Idendity 2. Я не знаю, чому спільнота Asp.Net не надає такого прикладу у статтях за замовчуванням на своїх веб-сайтах.
oneNiceFriend

17

Я шукав таке саме рішення, і Пауел дав мені 99% відповіді. Єдине, чого мені не вистачало для відображення розширення, - це додавання наступного коду Razor на сторінку cshtml (view):

@using programname.Models.Extensions

Я шукав FirstName, який відображатиметься в правому верхньому куті мого NavBar після входу користувача.

Я думав, що опублікую цей випадок, він допомагає комусь іншому, Тож ось мій код:

Я створив нову папку під назвою "Розширення" (під папкою "Мої моделі") та створив новий клас як вказаний вище 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

Ознайомтеся з цим чудовим повідомленням у блозі Джона Аттена: 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;

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

2
Слід зазначити, що GetUserAsync(User)буде запитувати базу даних для отримання OrganiId. На відміну від цього, прийняте рішення буде включати OrganId у формулі заявки (наприклад, cookie). Перевага витягування цієї інформації з бази даних полягає в тому, що люди можуть переміщуватися між організаціями, не вимагаючи від них виходу / входу. Звичайно, недоліком є ​​те, що він потребує додаткового запиту до бази даних.
Метт

1

Dhaust дає хороший спосіб додати властивість до класу ApplicationUser. З огляду на код ОП, здається, вони, можливо, зробили це чи були на шляху до цього. Питання задається

Як я можу отримати властивість OrganId поточно зареєстрованого користувача у контролера? Однак OrganiId не є власністю, доступною в User.Identity. Чи потрібно мені розширити User.Identity, щоб включити властивість OrganId?

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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.