So erweitern Sie die verfügbaren Eigenschaften von User.Identity

130

Ich verwende MVC5 Identity 2.0, damit sich Benutzer bei meiner Website anmelden können, auf der die Authentifizierungsdetails in einer SQL-Datenbank gespeichert sind. Asp.net Identity wurde standardmäßig implementiert, wie in vielen Online-Tutorials zu finden ist.

Die ApplicationUser-Klasse in IdentityModels wurde um einige benutzerdefinierte Eigenschaften erweitert, z. B. eine ganzzahlige Organisations-ID. Die Idee ist, dass viele Benutzer für Datenbankbeziehungszwecke erstellt und einer gemeinsamen Organisation zugewiesen werden können.

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

Wie kann ich die OrganizationId-Eigenschaft des aktuell angemeldeten Benutzers von einem Controller aus abrufen? Ist dies über eine Methode verfügbar, sobald ein Benutzer angemeldet ist, oder muss ich die Organisations-ID jedes Mal, wenn eine Controller-Methode ausgeführt wird, basierend auf der Benutzer-ID aus der Datenbank abrufen?

Beim Lesen im Web muss ich Folgendes verwenden, um die angemeldete Benutzer-ID usw. zu erhalten.

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

OrganizationId ist jedoch keine in User.Identity verfügbare Eigenschaft. Muss ich User.Identity erweitern, um die OrganizationId-Eigenschaft einzuschließen? Wenn ja, wie gehe ich vor?

Der Grund, warum ich die Organisations-ID so oft benötige, ist, dass viele Tabellenabfragen von der Organisations-ID abhängen, um Daten abzurufen, die für die Organisation relevant sind und dem angemeldeten Benutzer zugeordnet sind.

RobHurd
quelle
3
Hilft Ihnen meine Antwort hier überhaupt?
Schuh
1
Ziemlich die gleiche Antwort von mir hier: stackoverflow.com/a/28138594/809357 - Wenn Sie diese Informationen regelmäßig im Leben der Anfrage benötigen, können Sie sie als Anspruch auf das Cookie setzen.
Trailmax
1
Danke @Shoe, beide Antworten haben funktioniert. Zusätzlich zu Ihren Antworten musste ich einen Anspruch hinzufügen, um im Cookie gespeichert zu werden. In der IdentityModels-Klasse musste ich userIdentity.AddClaim hinzufügen (neuer Anspruch ("MyApp: OrganizationId", OrganizationId.ToString ())); an die öffentliche asynchrone Task <ClaimsIdentity> GenerateUserIdentityAsync- Methode (UserManager <ApplicationUser> -Manager) .
RobHurd

Antworten:

219

Wenn Sie die Eigenschaften von User.Identity um zusätzliche Eigenschaften wie die obige Frage erweitern möchten, fügen Sie diese Eigenschaften zunächst wie folgt zur ApplicationUser-Klasse hinzu:

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

Dann müssen Sie eine Erweiterungsmethode wie diese erstellen (ich erstelle meine in einem neuen Erweiterungsordner):

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

Wenn Sie die Identität in der ApplicationUser-Klasse erstellen, fügen Sie einfach die Claim -> OrganizationId wie folgt hinzu:

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

Wenn Sie den Anspruch hinzugefügt und Ihre Erweiterungsmethode eingerichtet haben, um ihn als Eigenschaft für Ihre User.Identity verfügbar zu machen, fügen Sie auf der Seite / Datei, auf die Sie zugreifen möchten, eine using-Anweisung hinzu :

in meinem Fall: using App.Extensions;innerhalb eines Controllers und @using. App.Extensionsmit einer .cshtml View-Datei.

BEARBEITEN:

Sie können auch vermeiden, dass in jeder Ansicht eine using-Anweisung hinzugefügt wird, indem Sie in den Ordner "Ansichten" wechseln und dort die Datei "Web.config" suchen. Suchen Sie nun nach dem <namespaces>Tag und fügen Sie dort Ihren Erweiterungs-Namespace wie folgt hinzu:

<add namespace="App.Extensions" />

Speichern Sie Ihre Datei und Sie sind fertig. Jetzt kennt jede Ansicht Ihre Erweiterungen.

Sie können auf die Erweiterungsmethode zugreifen:

var orgId = User.Identity.GetOrganizationId();
Pawel
quelle
Hallo pawel, ich habe das gleiche versucht, aber Fehler erhalten, dass Anwendungsbenutzer keine Definition von OrganisationId enthält
Es ist eine Falle
@RachitGupta Wann passiert es? Wenn Sie versuchen, den Anspruch hinzuzufügen, oder wenn Sie später im Code versuchen, auf seinen Wert zuzugreifen? Wenn Sie einen Anspruch hinzufügen, stellen Sie sicher, dass in Ihrem ApplicationUser die Eigenschaft definiert ist. Wenn dies später im Code der Fall ist, vergessen Sie nicht, die using-Anweisung an der Stelle hinzuzufügen, an der Sie die Erweiterungsmethode erstellt haben, z. B.: Using App.Extensions ;;
Pawel
Fehler in der Zeile userIdentity.AddClaim. Ich habe die IdentityExtensions-Klasse nur in der Datei IdentityModels.cs erstellt. Kann das eine Problemquelle sein?
Es ist eine Falle
Nein, wenn Sie den Anspruch hinzufügen, geschieht dies lange bevor identitätserweiterungen ins Spiel kommen (dann lesen Sie den Wert zurück) ... Stellen Sie sicher, dass Ihr ApplicationUser über die Eigenschaft verfügt, die Sie als Anspruch hinzufügen möchten: In diesem Beispiel war dies der Fall public long OrganizationId {get; einstellen; }
Pawel
6
Danke, es hat funktioniert. Ich denke, dies ist die beste Vorgehensweise für benutzerdefinierte Variablen in Asp Net Idendity 2. Ich weiß nicht, warum die Asp.Net-Community in Standardartikeln auf ihren Websites kein solches Beispiel bereitstellt.
oneNiceFriend
17

Ich suchte nach der gleichen Lösung und Pawel gab mir 99% der Antwort. Das einzige, was fehlte, damit die Erweiterung angezeigt werden konnte, war das Hinzufügen des folgenden Razor-Codes zur cshtml-Seite (Ansicht):

@using programname.Models.Extensions

Ich habe nach dem Vornamen gesucht, der oben rechts in meiner NavBar angezeigt wird, nachdem sich der Benutzer angemeldet hat.

Ich dachte, ich würde dies posten, falls es jemand anderem hilft. Also hier ist mein Code:

Ich habe einen neuen Ordner mit dem Namen "Erweiterungen" (unter meinem Modellordner) erstellt und die neue Klasse als oben angegebenes Pawel erstellt: 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;
    }
}

Dann habe ich in meinen _LoginPartial.cshtml(Unter Views/SharedOrdnern) hinzugefügt@using.ProgramName.Models.Extensions

Ich habe dann die Änderung zu der folgenden Codezeile hinzugefügt, die nach dem Anmelden den Vornamen des Benutzers verwenden sollte:

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

Vielleicht hilft das jemand anderem auf der ganzen Linie.

AxleWack
quelle
11

Lesen Sie diesen großartigen Blog-Beitrag von John Atten: ASP.NET Identity 2.0: Anpassen von Benutzern und Rollen

Es enthält großartige Schritt-für-Schritt-Informationen zum gesamten Prozess. Geh und lies es :)

Hier sind einige der Grundlagen.

Erweitern Sie die Standard-ApplicationUser-Klasse, indem Sie neue Eigenschaften hinzufügen (z. B. Adresse, Stadt, Bundesland usw.):

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

Anschließend fügen Sie Ihrem RegisterViewModel Ihre neuen Eigenschaften hinzu.

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

Aktualisieren Sie anschließend die Registeransicht, um die neuen Eigenschaften einzuschließen.

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

Aktualisieren Sie dann die Register () -Methode in AccountController mit den neuen Eigenschaften.

    // Add the Address properties:
    user.Address = model.Address;
    user.City = model.City;
    user.State = model.State;
    user.PostalCode = model.PostalCode;
Auspuff
quelle
16
Dies ist ein gutes Beispiel, aber es beantwortet nicht die Frage, wie Sie diese neuen Eigenschaften von User.Identity erhalten.
Dejan Bogatinovski
5
Heruntergestuft, da die Antwort nicht zeigt, wie die benutzerdefinierten Eigenschaften von User.Identity abgerufen werden können.
maulik13
3

Für alle, die diese Frage finden und nach dem Zugriff auf benutzerdefinierte Eigenschaften in ASP.NET Core 2.1 suchen, ist dies viel einfacher: Sie haben einen UserManager, z. B. in _LoginPartial.cshtml, und können dies dann einfach tun (vorausgesetzt, "ScreenName" ist a Eigenschaft, die Sie Ihrem eigenen AppUser hinzugefügt haben, der von IdentityUser erbt):

@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
quelle
2
Es sollte beachtet werden, dass GetUserAsync(User)die Datenbank abgefragt wird, um die Organisations-ID abzurufen. Im Gegensatz dazu enthält die akzeptierte Lösung die Organisations-ID in den Ansprüchen (z. B. Cookie). Der Vorteil des Abrufs dieser Informationen aus der Datenbank besteht darin, dass Personen zwischen Organisationen verschoben werden können, ohne dass sie sich abmelden / anmelden müssen. Der Nachteil ist natürlich, dass eine zusätzliche Datenbankabfrage erforderlich ist.
Matt
1

Dhaust bietet eine gute Möglichkeit, die Eigenschaft zur ApplicationUser-Klasse hinzuzufügen. Wenn man sich den OP-Code ansieht, scheint es, dass sie dies getan haben oder auf dem richtigen Weg waren, dies zu tun. Die Frage fragt

Wie kann ich die OrganizationId-Eigenschaft des aktuell angemeldeten Benutzers von einem Controller aus abrufen? OrganizationId ist jedoch keine in User.Identity verfügbare Eigenschaft. Muss ich User.Identity erweitern, um die OrganizationId-Eigenschaft einzuschließen?

Pawel bietet eine Möglichkeit, eine Erweiterungsmethode hinzuzufügen, für die Anweisungen verwendet oder der Namespace zur Datei web.config hinzugefügt werden muss.

In der Frage wird jedoch gefragt, ob Sie User.Identity um die neue Eigenschaft erweitern müssen. Es gibt eine alternative Möglichkeit, auf die Eigenschaft zuzugreifen, ohne User.Identity zu erweitern. Wenn Sie die Dhaust-Methode befolgt haben, können Sie den folgenden Code in Ihrem Controller verwenden, um auf die neue Eigenschaft zuzugreifen.

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

Ich hatte auch zusätzliche Spalten zu meiner AspNetUsers-Tabelle hinzugefügt oder erweitert. Als ich diese Daten einfach anzeigen wollte, fand ich viele Beispiele wie den obigen Code mit "Erweiterungen" usw. Dies hat mich wirklich erstaunt, dass Sie all diese Codezeilen schreiben mussten, um ein paar Werte von den aktuellen Benutzern zu erhalten.

Es stellt sich heraus, dass Sie die AspNetUsers-Tabelle wie jede andere Tabelle abfragen können:

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