So erhalten Sie den aktuellen Benutzer in asp.net core

128

Ich möchte einen aktuellen Benutzer zum Abrufen von Informationen eines Benutzers wie z. B. einer E-Mail erhalten. Aber das kann ich im asp.net-Kern nicht. Ich bin so verwirrt. Dies ist mein Code.

HttpContextIm Konstruktor des Controllers ist fast null . Es ist nicht gut, einen Benutzer für jede Aktion zu gewinnen. Ich möchte einmal Informationen über den Benutzer erhalten und sie einstellen ViewData.

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Mehran Hafizi
quelle
5
Verwenden Sie mit MVC oder Web APi?
Tushar

Antworten:

170
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT für Konstruktor

Der folgende Code funktioniert:

public Controller(IHttpContextAccessor httpContextAccessor)
{
    var userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value 
}

Für RTM bearbeiten

Sie sollten sich registrieren IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
adem caglin
quelle
2
Es funktioniert in Aktionen. Aber ich möchte im Konstruktor des Controllers verwenden.
Mehran Hafizi
3
Ist es möglich, dies in Klassen zu verwenden?
Mehran Hafizi
5
ClaimTypes.NameIdentifiergibt die aktuelle Benutzer-ID und ClaimTypes.Nameden Benutzernamen an.
Nikolay Kostov
3
Kann mir jemand sagen, was mit UserPrincipal.Current.Name nicht stimmt?
Tipura
2
@ademcaglin Aus bestimmten Gründen kehrt der Benutzer nullin meinem Fall zurück? Ich benutze .Net core 2.1 Web apiaber.
Sruthi Varghese
53

Einfache Art und Weise, die funktioniert und ich habe überprüft.

private readonly UserManager<IdentityUser> _userManager;
public CompetitionsController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);

dann können Sie alle Eigenschaften dieser Variablen wie user.Email. Ich hoffe das würde jemandem helfen.

Bearbeiten :

Es ist eine scheinbar einfache, aber etwas komplizierte Ursache für verschiedene Arten von Authentifizierungssystemen in ASP.NET Core. Ich aktualisiere, weil einige Leute bekommen null.

Für die JWT-Authentifizierung (getestet unter ASP.NET Core v3.0.0-Vorschau7):

var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

var user = await _userManager.FindByEmailAsync(email);
Ahmad
quelle
1
funktioniert gut für mich in einem Controller in asp.net Core 2.0
jmdon
2
Was ist ein _userManager?
NullVoxPopuli
6
In ASP.NET Core Identity ist User Manager ein Dienst, der von Dependency Inject zum Erstellen von Benutzern bereitgestellt wird. Weitere Informationen finden Sie in den Dokumenten :
Ahmad
2
Wie kann dies mit einer nicht asynchronen Methode erreicht werden?
T3.0
Für mich gibt null zurück. Warum?
Alberto Cláudio Mandlate
22

Haben Sie eine andere Möglichkeit, den aktuellen Benutzer in Asp.NET Core zu finden - und ich glaube, ich habe es hier irgendwo auf SO ^^ gesehen

// Stores UserManager
private readonly UserManager<ApplicationUser> _manager; 

// Inject UserManager using dependency injection.
// Works only if you choose "Individual user accounts" during project creation.
public DemoController(UserManager<ApplicationUser> manager)  
{  
    _manager = manager;  
}

// You can also just take part after return and use it in async methods.
private async Task<ApplicationUser> GetCurrentUser()  
{  
    return await _manager.GetUserAsync(HttpContext.User);  
}  

// Generic demo method.
public async Task DemoMethod()  
{  
    var user = await GetCurrentUser(); 
    string userEmail = user.Email; // Here you gets user email 
    string userId = user.Id;
}  

Dieser Code geht an den Controller namens DemoController. Funktioniert nicht ohne beide zu warten (wird nicht kompiliert);)

Hekkaryk
quelle
Dies erfordert die Verwendung von Identity
Fraze
1
Was ist ApplicationUser?
Mike
ApplicationUser wird normalerweise von IdentityUser geerbt, sodass es um zusätzliche Eigenschaften usw. erweitert werden kann.
Corgalore
20

Ich muss sagen, dass ich ziemlich überrascht war, dass HttpContext im Konstruktor null ist. Ich bin sicher, es ist aus Leistungsgründen. Habe bestätigt, dass die Verwendung IPrincipalwie unten beschrieben in den Konstruktor injiziert wird. Es macht im Wesentlichen das Gleiche wie die akzeptierte Antwort, aber auf eine andere Art und Weise.


Für alle, die diese Frage finden und nach einer Antwort auf das allgemeine "Wie bekomme ich den aktuellen Benutzer?" Suchen. Sie können einfach Userdirekt von zugreifen Controller.User. Sie können dies jedoch nur innerhalb von Aktionsmethoden tun (ich gehe davon aus, dass Controller nicht nur mit HttpContexts und aus Leistungsgründen ausgeführt werden).

Wenn Sie es jedoch im Konstruktor benötigen (wie es OP getan hat) oder andere injizierbare Objekte erstellen müssen, die den aktuellen Benutzer benötigen, ist das Folgende ein besserer Ansatz:

Injizieren Sie IPrincipal, um den Benutzer zu erhalten

Erstes Treffen IPrincipalundIIdentity

public interface IPrincipal
{
    IIdentity Identity { get; }
    bool IsInRole(string role);
}

public interface IIdentity
{
    string AuthenticationType { get; }
    bool IsAuthenticated { get; }
    string Name { get; }
}

IPrincipalund IIdentityrepräsentiert den Benutzer und den Benutzernamen. Wikipedia wird Sie trösten, wenn "Principal" seltsam klingt .

Wichtig zu erkennen , dass , ob Sie es aus IHttpContextAccessor.HttpContext.User, ControllerBase.Useroder ControllerBase.HttpContext.UserSie bekommen ein Objekt , das eine sein , garantiert ClaimsPrincipalObjekt , das implementiertIPrincipal .

Es gibt derzeit keinen anderen Benutzertyp, den ASP.NET verwendet User(aber das heißt nicht, dass etwas anderes nicht implementiert werden kann IPrincipal).

Wenn Sie also etwas haben, das von 'dem aktuellen Benutzernamen' abhängt, den Sie injizieren möchten, sollten Sie injizieren IPrincipalund definitiv nicht IHttpContextAccessor.

Wichtig: Verschwenden Sie keine Zeit damit, IPrincipaldirekt in Ihren Controller oder Ihre Aktionsmethode zu injizieren - dies ist sinnlos, da Useres Ihnen dort bereits zur Verfügung steht.

In startup.cs:

   // Inject IPrincipal
   services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);

Dann in Ihr DI-Objekt, das den Benutzer benötigt, injizieren Sie einfach, IPrincipalum den aktuellen Benutzer zu erhalten.

Das Wichtigste dabei ist, wenn Sie Unit-Tests durchführen, müssen Sie keine senden HttpContext, sondern nur etwas verspotten, das darstellt, IPrincipal was einfach sein kann ClaimsPrincipal .

Eine besonders wichtige Sache, bei der ich mir nicht 100% sicher bin. Wenn Sie die tatsächlichen Ansprüche zugreifen müssen aus ClaimsPrincipalmüssen Sie Guss IPrincipalzu ClaimsPrincipal. Dies ist in Ordnung, da wir zu 100% wissen, dass es zur Laufzeit von diesem Typ ist (da dies der Fall HttpContext.Userist). Eigentlich mache ich das einfach gerne im Konstruktor, da ich sicher schon weiß, dass IPrincipal es eine sein wird ClaimsPrincipal.

Wenn Sie sich lustig machen, erstellenClaimsPrincipal Sie einfach ein direktes und übergeben Sie es an alles, was Sie brauchen IPrincipal.

Warum es keine Schnittstelle gibt, weiß IClaimsPrincipalich nicht genau . Ich gehe davon aus, dass MS entschieden hat, dass ClaimsPrincipales sich nur um eine spezialisierte „Sammlung“ handelt, für die keine Schnittstelle erforderlich ist.

Simon_Weaver
quelle
2
Auf diese Weise können Sie den aktuellen Benutzer an einer beliebigen Stelle in Ihrer Anwendung einfügen. Eine großartige Antwort!
Machado
1
Das funktioniert nicht. Ich bekomme immer nullfür die injiziert IPrincipal. Ich musste den vorübergehenden Dienst auch als …GetService<IHttpContextAccessor>()?.HttpContext.User…(mit dem ?) hinzufügen, da er sonst abstürzen würde (GetService gab null zurück).
Ygoe
Sie können einfach services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);HttpContext.User ist ein ClaimsPrincipal.
Jay Zelos
18

Es scheint, dass ab sofort (April 2017) Folgendes funktioniert:

public string LoggedInUser => User.Identity.Name;

Zumindest während eines Controller

Grandizer
quelle
4
Sie können den Typ 'System.Security.Principal.IIdentity' nicht implizit in 'string' konvertieren.
Anthony Huang
3
Zeichenfolge LoggedInUser = User.Identity.Name;
Alic W
5
Als jemand, der den zuvor =>verwendeten Operator noch nicht gesehen hat , wird er als "Ausdruckskörperdefinition" bezeichnet und in dieser Dokumentation beschrieben . Nur für den Fall, dass sich zukünftige Leute wie ich fragen.
Nathan Clement
Ihr Code konnte möglicherweise vor der Bearbeitung nicht kompiliert werden, da keine Konvertierung von IIdentitynach erfolgt string, wie auch im oberen Kommentar angegeben. Die Bearbeitung hat das einfach behoben. Ich bin mir auch nicht sicher, wie Sie zu Ihrer Schlussfolgerung gekommen sind (insbesondere, weil "Editor" -Punkte nur Benutzern mit einem Ruf von weniger als 2.000 Punkten gegeben werden).
Fuglede
9

Vielleicht habe ich die Antwort nicht gesehen, aber so mache ich es.

  1. .Net Core -> Eigenschaften -> launchSettings.json

Sie müssen diese Werte ändern

"windowsAuthentication": true, // needs to be true
"anonymousAuthentication": false,  // needs to be false 

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC oder Web Api Controller

private readonly IHttpContextAccessor _httpContextAccessor;
//constructor then
_httpContextAccessor = httpContextAccessor;

Controller-Methode:

string userName = _httpContextAccessor.HttpContext.User.Identity.Name;

Ergebnis ist Benutzername, zB = Domain \ Benutzername

Tom Stickel
quelle
4

Mein Problem bestand darin, auf den angemeldeten Benutzer als Objekt in der cshtml-Datei zuzugreifen. In Anbetracht der Tatsache, dass Sie den Benutzer in ViewData haben möchten, kann dieser Ansatz hilfreich sein:

In der cshtml-Datei

@using Microsoft.AspNetCore.Identity
@inject UserManager<ApplicationUser> UserManager

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
    @UserManager.FindByNameAsync(UserManager.GetUserName(User)).Result.Email
    </title>
  </head>
  <body>

  </body>
</html>
MikeMajara
quelle
Jede Idee, wie Sie eine Navigationseigenschaft zum Laden erhalten könnten (Firmenname einer Firmennavigationseigenschaft in meiner ApplicationUser-Klasse). Es wurde keine Möglichkeit gefunden, Navigationseigenschaften einzuschließen.
Hunter Nelson
1

Zusätzlich zu den vorhandenen Antworten möchte ich hinzufügen, dass Sie auch eine Klasseninstanz für die gesamte App verfügbar haben können, die benutzerbezogene Daten wie UserIDusw. enthält.

Es kann nützlich sein , Refactoring z.B. Sie möchten nicht UserIDjede Controller-Aktion abrufen und UserIDin jeder Methode, die sich auf Service Layer bezieht , einen zusätzlichen Parameter deklarieren .

Ich habe recherchiert und hier ist mein Beitrag .

Sie erweitern einfach Ihre Klasse, von der Sie abgeleitet sind, DbContextindem Sie eine UserIdEigenschaft hinzufügen (oder eine benutzerdefinierte SessionKlasse implementieren, die diese Eigenschaft hat).

Auf Filterebene können Sie Ihre Klasseninstanz abrufen und den UserIdWert festlegen .

Danach, wo immer Sie Ihre Instanz injizieren - sie hat die erforderlichen Daten (die Lebensdauer muss pro Anforderung sein , also registrieren Sie sie mit der AddScopedMethode).

Arbeitsbeispiel:

public class AppInitializationFilter : IAsyncActionFilter
{
    private DBContextWithUserAuditing _dbContext;

    public AppInitializationFilter(
        DBContextWithUserAuditing dbContext
        )
    {
        _dbContext = dbContext;
    }

    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next
        )
    {
        string userId = null;
        int? tenantId = null;

        var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;

        var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
        if (userIdClaim != null)
        {
            userId = userIdClaim.Value;
        }

        var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
        if (tenantIdClaim != null)
        {
            tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
        }

        _dbContext.UserId = userId;
        _dbContext.TenantId = tenantId;

        var resultContext = await next();
    }
}

Weitere Informationen finden Sie in meiner Antwort .

Alex Herman
quelle
0

Nehmen IdentityUserwürde auch funktionieren. Dies ist ein aktuelles Benutzerobjekt, und alle Benutzerwerte können abgerufen werden.

private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);
zzdhxu
quelle
0

Wenn Sie die gerüstete Identität verwenden und Asp.net Core 2.2+ verwenden, können Sie über eine Ansicht wie folgt auf den aktuellen Benutzer zugreifen:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

 @if (SignInManager.IsSignedIn(User))
    {
        <p>Hello @User.Identity.Name!</p>
    }
    else
    {
        <p>You're not signed in!</p>
    }

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio

Andrew
quelle
0

Dies ist eine alte Frage, aber mein Fall zeigt, dass mein Fall hier nicht besprochen wurde.

Die Antwort von Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ) gefällt mir am besten . Er erklärt ausführlich, wie man mit IPrincipal und IIdentity einen Benutzernamen erhält. Diese Antwort ist absolut richtig und ich empfehle diesen Ansatz. Während des Debuggens trat jedoch das Problem auf, dass ASP.NET das Dienstprinzip NICHT ordnungsgemäß ausfüllen kann . (oder mit anderen Worten, IPrincipal.Identity.Name ist null)

Es ist offensichtlich, dass MVC Framework, um den Benutzernamen zu erhalten, es von irgendwoher nehmen sollte. In der .NET-Welt verwendet ASP.NET oder ASP.NET Core die Open ID Connect-Middleware. Im einfachen Szenario authentifizieren Web-Apps einen Benutzer in einem Webbrowser. In diesem Szenario weist die Webanwendung den Browser des Benutzers an, sich bei Azure AD anzumelden. Azure AD gibt über den Browser des Benutzers eine Anmeldeantwort zurück, die Angaben zum Benutzer in einem Sicherheitstoken enthält. Damit es im Code für Ihre Anwendung funktioniert, müssen Sie die Berechtigung angeben, an die sich Ihre Web-App-Delegierten anmelden. Wenn Sie Ihre Webanwendung für Azure Service bereitstellen, besteht das übliche Szenario zur Erfüllung dieser Anforderungen darin, die Webanwendung zu konfigurieren: "App Services" -> YourApp -> Blade "Authentifizierung / Autorisierung" -> "App Service Authenticatio" = "On"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Ich glaube (dies ist meine Vermutung), dass der Assistent unter der Haube dieses Prozesses die "übergeordnete" Webkonfiguration dieser Web-App anpasst, indem er dieselben Einstellungen hinzufügt, die ich in den folgenden Absätzen zeige. Grundsätzlich besteht das Problem, warum dieser Ansatz in ASP.NET Core NICHT funktioniert, darin, dass die "übergeordnete" Computerkonfiguration von webconfig ignoriert wird. (Dies ist nicht 100% sicher, ich gebe nur die beste Erklärung, die ich habe). Damit es funktioniert, müssen Sie dies manuell in Ihrer App einrichten.

In diesem Artikel wird erläutert, wie Sie Ihre App für die Verwendung von Azure AD mehrfach einrichten. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Schritt 1: Registrieren Sie das Beispiel bei Ihrem Azure AD-Mandanten. (Es ist offensichtlich, ich möchte meine Zeit nicht mit Erklärungen verbringen).

Schritt 2: Ersetzen Sie in der Datei appsettings.json den ClientID-Wert durch die Anwendungs-ID der Anwendung, die Sie in Schritt 1 im Anwendungsregistrierungsportal registriert haben. Ersetzen Sie den TenantId-Wert durch common

Schritt 3: Öffnen Sie die Datei Startup.cs und fügen Sie in der ConfigureServices-Methode nach der Zeile mit .AddAzureAD den folgenden Code ein, mit dem Ihre Anwendung Benutzer mit dem Azure AD v2.0-Endpunkt anmelden kann, dh sowohl Work als auch School und Microsoft Personal-Konten.

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
});

Zusammenfassung : Ich habe ein weiteres mögliches Problem aufgezeigt, das zu einem Fehler führen könnte, bei dem der Themenstarter erklärt wird. Der Grund für dieses Problem sind fehlende Konfigurationen für Azure AD (Open ID Middleware). Um dieses Problem zu lösen, schlage ich vor, "Authentifizierung / Autorisierung" manuell einzurichten. Die kurze Übersicht über die Einrichtung wird hinzugefügt.

Roman Oira-Oira
quelle
0

Die meisten Antworten zeigen, wie man am besten mit HttpContextder Dokumentation umgeht, und das habe ich auch getan.

Ich wollte erwähnen, dass Sie Ihre Projekteinstellungen beim Debuggen überprüfen möchten. Die Standardeinstellung ist Enable Anonymous Authentication = true.

Juan Emmanuel Afable
quelle
-1

Ich habe meine Lösung

var claim = HttpContext.User.CurrentUserID();

public static class XYZ
{
    public static int CurrentUserID(this ClaimsPrincipal claim)
    {
        var userID = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
         "UserID").Value;
        return Convert.ToInt32(userID);
    }
    public static string CurrentUserRole(this ClaimsPrincipal claim)
    {
        var role = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
        "Role").Value;
        return role;
    }
}
neeraj rai
quelle
1
Während dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und warum das Problem gelöst wird, den langfristigen Wert der Antwort verbessern.
Alexander