web-dev-qa-db-fra.com

Comment étendre les propriétés disponibles de User.Identity

J'utilise MVC5 Identity 2.0 pour permettre aux utilisateurs de se connecter à mon site Web, où les détails d'authentification sont stockés dans une base de données SQL. Asp.net Identity a été mis en œuvre de manière standard, comme dans de nombreux tutoriels en ligne.

La classe ApplicationUser dans IdentityModels a été étendue pour inclure certaines propriétés personnalisées, telles qu'un entier OrganizationId. L'idée est que de nombreux utilisateurs peuvent être créés et attribués à une organisation commune à des fins de relation avec la base de données.

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

Comment puis-je récupérer la propriété OrganizationId de l'utilisateur actuellement connecté à partir d'un contrôleur? Est-ce disponible via une méthode une fois qu'un utilisateur est connecté ou est-ce que je dois toujours récupérer le OrganizationId de la base de données, en fonction du UserId, chaque fois qu'une méthode de contrôleur s'exécute?

En lisant sur le Web, j’ai vu que j’avais besoin d’utiliser ce qui suit pour obtenir l’utilisateur connecté, etc.

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

Toutefois, OrganizationId n'est pas une propriété disponible dans User.Identity. Dois-je étendre User.Identity pour inclure la propriété OrganizationId? Si oui, comment puis-je m'y prendre?.

La raison pour laquelle j'ai si souvent besoin de l'OrganizationId est que de nombreuses requêtes de table dépendent d'OrganizationId pour extraire les données pertinentes pour l'organisation associée à l'utilisateur connecté.

120
RobHurd

Chaque fois que vous souhaitez étendre les propriétés de User.Identity avec des propriétés supplémentaires telles que la question ci-dessus, ajoutez d'abord ces propriétés à la classe ApplicationUser de la manière suivante:

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

Ensuite, vous devez créer une méthode d’extension semblable à celle-ci (je crée la mienne dans un nouveau dossier 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;
        }
    }
}

Lorsque vous créez l'identité dans la classe ApplicationUser, ajoutez simplement Claim -> OrganizationId comme suit:

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

Une fois que vous avez ajouté la revendication et que votre méthode d'extension est en place, pour la rendre disponible en tant que propriété de votre User.Identity, ajoutez une instruction using sur la page/le fichier auquel vous souhaitez accéder:

dans mon cas: using App.Extensions; dans un contrôleur et @using. App.Extensions avec un fichier .cshtml View.

MODIFIER:

Pour éviter d'ajouter une instruction using dans chaque vue, vous pouvez également accéder au dossier Vues et y localiser le fichier Web.config. Recherchez maintenant la balise <namespaces> et ajoutez votre espace de noms d'extension comme suit:

<add namespace="App.Extensions" />

Enregistrez votre fichier et vous avez terminé. Désormais, chaque vue saura de vos extensions.

Vous pouvez accéder à la méthode d'extension:

var orgId = User.Identity.GetOrganizationId();

J'espère que ça aide n'importe qui :)

206
Pawel

Je cherchais la même solution et Pawel m'a donné 99% de la réponse. La seule chose qui me manquait pour que l'extension s'affiche, a été d'ajouter le code de rasoir suivant à la page cshtml (vue):

@using programname.Models.Extensions

Je cherchais le prénom, à afficher en haut à droite de ma barre de navigation après la connexion de l'utilisateur.

Je pensais que je posterais ceci si cela aide quelqu'un d'autre, donc voici mon code:

J'ai créé un nouveau dossier appelé Extensions (Under my Models Folder) et créé la nouvelle classe comme Pawel spécifié ci-dessus: 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;
    }
}

Puis dans mon _LoginPartial.cshtml (Sous Views/Shared Dossiers) j'ai ajouté @using.ProgramName.Models.Extensions

J'ai ensuite ajouté le changement à la ligne de code suivante qui allait utiliser le nom d'utilisateur après la connexion:

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

Peut-être que cela aide quelqu'un d'autre sur la ligne.

16
AxleWack

Découvrez ce billet de blog de John Atten: ASP.NET Identity 2.0: Personnalisation des utilisateurs et des rôles

Il contient d'excellentes informations étape par étape sur l'ensemble du processus. Allez le lire:)

Voici quelques bases.

Étendez la classe ApplicationUser par défaut en ajoutant de nouvelles propriétés (par exemple, adresse, ville, état, etc.):

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

Ensuite, vous ajoutez vos nouvelles propriétés à votre RegisterViewModel.

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

Puis mettez à jour la vue de registre pour inclure les nouvelles propriétés.

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

Ensuite, mettez à jour la méthode Register () sur AccountController avec les nouvelles propriétés.

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

Pour tous ceux qui trouvent cette question cherchant comment accéder à des propriétés personnalisées dans ASP.NET Core 2.1, c'est beaucoup plus simple: vous aurez un UserManager, par exemple. Dans _LoginPartial.cshtml, vous pouvez simplement procéder (en supposant que "ScreenName" est une propriété que vous avez ajoutée à votre propre AppUser qui hérite d'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
Jashan

Dhaust fournit un bon moyen d'ajouter la propriété à la classe ApplicationUser. En regardant le code OP, il semble qu'ils ont peut-être fait ceci ou étaient sur la bonne voie pour le faire. La question demande

Comment puis-je récupérer la propriété OrganizationId de l'utilisateur actuellement connecté à partir d'un contrôleur? Toutefois, OrganizationId n'est pas une propriété disponible dans User.Identity. Dois-je étendre User.Identity pour inclure la propriété OrganizationId?

Pawel offre un moyen d'ajouter une méthode d'extension nécessitant l'utilisation d'instructions ou l'ajout d'un espace de nom au fichier web.config.

Cependant, la question vous demande si vous "devez" étendre User.Identity pour inclure la nouvelle propriété. Il existe un autre moyen d'accéder à la propriété sans étendre User.Identity. Si vous avez suivi la méthode Dhaust, vous pouvez ensuite utiliser le code suivant dans votre contrôleur pour accéder à la nouvelle propriété.

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

J'avais également ajouté ou étendu des colonnes supplémentaires dans ma table AspNetUsers. Lorsque je voulais simplement visualiser ces données, j'ai trouvé de nombreux exemples, tels que le code ci-dessus avec "Extensions", etc.

Il s'avère que vous pouvez interroger la table AspNetUsers comme n'importe quelle autre table:

 ApplicationDbContext db = new ApplicationDbContext();
 var user = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault();
0
Anthony Griggs