Come ottenere l'utente corrente in asp.net core


129

Voglio ottenere un utente corrente per ottenere informazioni su un utente come un'e-mail. Ma non posso farlo in asp.net core. Sono così confuso Questo è il mio codice.

HttpContextquasi è nullo nel costruttore del controller. Non è bene avere un utente in ogni azione. Voglio ottenere le informazioni dell'utente una volta e impostarle in ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}

5
Usando con MVC o Web APi?
Tushar

Risposte:


172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT per il costruttore

Di seguito il codice funziona:

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

Modifica per RTM

Dovresti registrarti IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }

2
funziona in azioni. ma voglio usare nel costruttore del controller.
Mehran Hafizi

3
è possibile usarlo nelle classi?
Mehran Hafizi

5
ClaimTypes.NameIdentifierfornisce l'id utente corrente e ClaimTypes.Namefornisce il nome utente.
Nikolay Kostov

3
Qualcuno può dirmi cosa c'è di sbagliato in UserPrincipal.Current.Name?
tipura

2
@ademcaglin Per alcuni motivi l'utente torna nullnel mio caso? Sto usando .Net core 2.1 Web apiperò.
Sruthi Varghese

55

Modo semplice che funziona e ho controllato.

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

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

quindi puoi tutte le proprietà di queste variabili come user.Email . Spero che questo possa aiutare qualcuno.

modificare :

È una cosa apparentemente semplice ma un po 'complicata causa di diversi tipi di sistemi di autenticazione in ASP.NET Core. Aggiornamento perché alcune persone stanno ricevendonull .

Per l'autenticazione JWT (testato su ASP.NET Core v3.0.0-preview7):

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

var user = await _userManager.FindByEmailAsync(email);

1
funziona alla grande per me all'interno di un controller in asp.net Core 2.0
jmdon

2
cos'è un _userManager?
NullVoxPopuli

6
In ASP.NET Core Identity, User Manager è un servizio fornito da Dependency Inject per creare utenti. Vedi i documenti per maggiori informazioni:
Ahmad

2
Come si può ottenere questo risultato con un metodo non asincrono?
T3.0

Per me sta tornando nullo. Perché?
Alberto Cláudio Mandlate

22

Hai un altro modo per ottenere l'utente corrente in Asp.NET Core e penso di averlo visto da qualche parte qui, su SO ^^

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

Quel codice va al controller denominato DemoController. Non funzionerà senza attenderli entrambi (non verranno compilati);)


Ciò richiede l'uso di Identity
Fraze

1
Cos'è ApplicationUser?
Mike

ApplicationUser viene in genere ereditato da IdentityUser quindi può essere esteso con proprietà aggiuntive, ecc.
Corgalore

20

Devo dire che sono rimasto piuttosto sorpreso dal fatto che HttpContext sia nullo all'interno del costruttore. Sono sicuro che sia per motivi di prestazioni. Ho confermato che l'utilizzo IPrincipalcome descritto di seguito lo fa iniettare nel costruttore. Sta essenzialmente facendo lo stesso della risposta accettata, ma in un modo più interfacciato.


Per chiunque trovi questa domanda cercando una risposta alla generica "Come ottenere l'utente corrente?" puoi semplicemente accedere Userdirettamente daController.User . Ma puoi farlo solo all'interno dei metodi di azione (presumo perché i controller non vengono eseguiti solo con HttpContexts e per motivi di prestazioni).

Tuttavia, se ne hai bisogno nel costruttore (come ha fatto OP) o hai bisogno di creare altri oggetti iniettabili che richiedono l'utente corrente, il seguente è un approccio migliore:

Iniettare IPrincipal per ottenere user

Primo incontro IPrincipaleIIdentity

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

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

IPrincipale IIdentityrappresenta l'utente e il nome utente. Wikipedia ti consolerà se "Preside" suona strano .

Importante rendersi conto che se si ottiene da IHttpContextAccessor.HttpContext.User, ControllerBase.Usero ControllerBase.HttpContext.Userche stai ricevendo un oggetto che è garantito per essere un ClaimsPrincipaloggetto che implementaIPrincipal .

Non c'è nessun altro tipo di utente che ASP.NET utilizza per Userora, (ma questo non vuol dire altro che qualcos'altro non potrebbe implementare IPrincipal).

Quindi, se hai qualcosa che ha una dipendenza del "nome utente corrente" che desideri venga iniettato, dovresti farlo IPrincipale sicuramente no IHttpContextAccessor.

Importante: non perdere tempo a iniettare IPrincipaldirettamente nel controller o nel metodo di azione: da allora è inutileUser è già disponibile.

In startup.cs:

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

Quindi nel tuo oggetto DI che ha bisogno dell'utente che hai appena iniettato IPrincipal per ottenere l'utente corrente.

La cosa più importante qui è che se stai facendo unit test non è necessario inviare un HttpContext, ma devi solo deridere qualcosa che rappresenta IPrincipal che può essere ClaimsPrincipal .

Una cosa importante in più di cui non sono sicuro al 100%. Se avete bisogno di accedere ai crediti effettivi da ClaimsPrincipaldovete getto IPrincipala ClaimsPrincipal. Questo va bene poiché sappiamo al 100% che in fase di esecuzione è di quel tipo (poiché è quello che HttpContext.Userè). In realtà mi piace farlo solo nel costruttore poiché so già per certo che qualsiasi IPrincipal sarà un fileClaimsPrincipal .

Se stai prendendo in giro, creaClaimsPrincipal semplicemente un direttamente e passalo a qualsiasi cosa IPrincipal.

Esattamente il motivo per cui non esiste un'interfaccia per IClaimsPrincipalnon ne sono sicuro. Presumo che MS abbia deciso che ClaimsPrincipalera solo una "raccolta" specializzata che non garantisce un'interfaccia.


2
Ciò ti consente di iniettare l'utente corrente ovunque nella tua applicazione, ottima risposta!
Machado

1
Questo non funziona. Ottengo sempre nullper l'iniezione IPrincipal. Avevo anche bisogno di aggiungere il servizio temporaneo come …GetService<IHttpContextAccessor>()?.HttpContext.User…(con ?) perché altrimenti si sarebbe bloccato (GetService restituiva null).
ygoe

Puoi semplicemente fare services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);come HttpContext.User è un ClaimsPrincipal.
Jay Zelos,

18

Sembrerebbe che a partire da ora (aprile 2017) che le seguenti funzioni:

public string LoggedInUser => User.Identity.Name;

Almeno mentre entro a Controller


4
Non è possibile convertire implicitamente il tipo "System.Security.Principal.IIdentity" in "stringa".
Anthony Huang

3
string LoggedInUser = User.Identity.Name;
Alic W

5
Poiché qualcuno che non aveva mai visto l' =>operatore utilizzato in questo modo prima, si chiama "Definizione del corpo dell'espressione" ed è descritto in questa documentazione . Nel caso in cui persone future come me se lo chiedano.
Nathan Clement,

Il tuo codice non può essere compilato prima della modifica, dato che non c'è conversione da IIdentitya string, come indicato anche nel commento in alto. La modifica lo ha semplicemente risolto. Non sono nemmeno sicuro di come tu sia arrivato alla tua conclusione (in particolare dal momento che i punti "editor" vengono assegnati solo agli utenti con una reputazione inferiore a 2k).
fuglede

9

Forse non ho visto la risposta, ma è così che lo faccio.

  1. .Net Core -> Proprietà -> launchSettings.json

È necessario modificare questi valori

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

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Controller MVC o API Web

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

Metodo controller:

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

Il risultato è userName eg = Domain \ username


4

Il mio problema era accedere all'utente connesso come oggetto nel file cshtml. Considerando che volevi l'utente in ViewData, questo approccio potrebbe essere utile:

Nel file cshtml

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

Qualche idea su come caricare una proprietà di navigazione (nome della società di una proprietà di navigazione della società nella mia classe ApplicationUser). Non ho trovato un modo per includere le proprietà di navigazione.
Hunter Nelson

1

Oltre alle risposte esistenti, vorrei aggiungere che puoi anche avere un'istanza di classe disponibile in tutta l'app che contiene dati relativi agli utenti come UserIDecc.

Può essere utile per il refactoring ad es. non si desidera recuperare UserIDin ogni azione del controller e dichiarare un UserIDparametro aggiuntivo in ogni metodo relativo al livello di servizio.

Ho fatto una ricerca ed ecco il mio post .

Devi solo estendere la tua classe da cui derivi DbContextaggiungendo UserIdproprietà (o implementando una Sessionclasse personalizzata che ha questa proprietà).

A livello di filtro puoi recuperare l'istanza della tua classe e impostare il UserIdvalore.

Dopodiché, ovunque tu inietti la tua istanza, avrà i dati necessari (la durata deve essere per richiesta , quindi la registri usando il AddScopedmetodo).

Esempio di lavoro:

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

Per ulteriori informazioni vedere la mia risposta .


0

Anche prendere IdentityUseravrebbe funzionato. Questo è un oggetto utente corrente e tutti i valori di utente possono essere recuperati.

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

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

0

Se stai usando Identity scafolded e usando Asp.net Core 2.2+ puoi accedere all'utente corrente da una vista come questa:

@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


0

Questa è una vecchia domanda, ma il mio caso mostra che il mio caso non è stato discusso qui.

Mi piace di più la risposta di Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Spiega in dettaglio come ottenere il nome utente utilizzando IPrincipal e IIdentity. Questa risposta è assolutamente corretta e consiglio di utilizzare questo approccio. Tuttavia, durante il debug ho riscontrato il problema quando ASP.NET NON può popolare correttamente il principio del servizio . (o in altre parole, IPrincipal.Identity.Name è null)

È ovvio che per ottenere il nome utente il framework MVC dovrebbe prenderlo da qualche parte. Nel mondo .NET, ASP.NET o ASP.NET Core utilizza il middleware Open ID Connect. Nello scenario semplice, le app Web autenticano un utente in un browser Web. In questo scenario, l'applicazione Web indica al browser dell'utente di accedervi ad Azure AD. Azure AD restituisce una risposta di accesso tramite il browser dell'utente, che contiene attestazioni sull'utente in un token di sicurezza. Per farlo funzionare nel codice della tua applicazione, dovrai fornire l'autorità a cui la tua app web delega l'accesso. Quando si distribuisce l'app Web nel servizio di Azure, lo scenario comune per soddisfare questi requisiti è configurare l'app Web: "Servizi app" -> YourApp -> "Autenticazione / Autorizzazione" blade -> "Autenticazione servizio app" = "On"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Credo (questa è la mia ipotesi plausibile) che sotto il cofano di questo processo la procedura guidata aggiusti la configurazione web "genitore" di questa app web aggiungendo le stesse impostazioni che mostro nei paragrafi seguenti. Fondamentalmente, il problema per cui questo approccio NON funziona in ASP.NET Core è perché la configurazione della macchina "padre" viene ignorata da webconfig. (questo non è sicuro al 100%, do solo la migliore spiegazione che ho). Quindi, per far sì che funzioni, devi configurarlo manualmente nella tua app.

Ecco un articolo che spiega come configurare manualmente la tua app per usare Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Passaggio 1: registrare l'esempio con il tenant di Azure AD. (è ovvio, non voglio sprecare il mio tempo in spiegazioni).

Passaggio 2: nel file appsettings.json: sostituire il valore ClientID con l'ID applicazione dall'applicazione registrata nel portale di registrazione dell'applicazione al passaggio 1. sostituire il valore TenantId con common

Passaggio 3: aprire il file Startup.cs e nel metodo ConfigureServices, dopo la riga contenente .AddAzureAD inserire il codice seguente, che consente all'applicazione di accedere agli utenti con l'endpoint Azure AD v2.0, ovvero sia Work che School e Account personali Microsoft.

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

Riepilogo : ho mostrato un altro possibile problema che potrebbe causare un errore durante la spiegazione dell'argomento. Il motivo di questo problema è la mancanza di configurazioni per Azure AD (middleware Open ID). Per risolvere questo problema propongo di impostare manualmente "Autenticazione / Autorizzazione". Viene aggiunta la breve panoramica su come configurarlo.


0

La maggior parte delle risposte mostra come gestire al meglio HttpContextla documentazione, che è anche quella con cui sono andato.

Volevo dire che ti consigliamo di controllare le impostazioni del progetto durante il debug, l'impostazione predefinita è Enable Anonymous Authentication = true.


-1

Ho la mia soluzione

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

1
Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo su come e perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Alexander
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.