Jak uzyskać bieżącego użytkownika w asp.net core

130

Chcę uzyskać aktualnego użytkownika w celu uzyskania informacji o użytkowniku, takich jak e-mail. Ale nie mogę tego zrobić w asp.net core. Jestem taki zdezorientowany. To jest mój kod.

HttpContextprawie ma wartość null w konstruktorze kontrolera. Nie jest dobrze przyciągać użytkownika do każdego działania. Chcę raz uzyskać informacje o użytkowniku i ustawić je w ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Mehran Hafizi
źródło
5
Używasz z MVC lub Web APi?
Tushar

Odpowiedzi:

173
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDYCJA dla konstruktora

Poniższy kod działa:

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

Edytuj dla RTM

Należy się zarejestrować IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
adem caglin
źródło
2
działa w actions.ale chcę użyć w konstruktorze kontrolera.
Mehran Hafizi
3
czy można to wykorzystać na zajęciach?
Mehran Hafizi
5
ClaimTypes.NameIdentifierpodaje aktualny identyfikator użytkownika i ClaimTypes.Namenazwę użytkownika.
Nikolay Kostov
3
Czy ktoś może mi powiedzieć, co jest nie tak z UserPrincipal.Current.Name?
tipura
2
@ademcaglin Z jakichś powodów Użytkownik wraca nullw moim przypadku? Ja .Net core 2.1 Web apijednak używam .
Sruthi Varghese,
55

Prosty sposób to działa i sprawdziłem.

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

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

wtedy możesz wszystkie właściwości tych zmiennych, takie jak user.Email. Mam nadzieję, że to komuś pomoże.

Edycja :

Jest to pozornie prosta, ale nieco skomplikowana przyczyna różnych typów systemów uwierzytelniania w ASP.NET Core. Aktualizuję, bo niektórzy dostają null.

W przypadku uwierzytelniania JWT (testowane na ASP.NET Core v3.0.0-preview7): For JWT Authentication (Tested on ASP.NET Core v3.0.0-preview7):

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

var user = await _userManager.FindByEmailAsync(email);
Ahmad
źródło
1
działa świetnie dla mnie wewnątrz kontrolera w asp.net Core 2.0
jmdon
2
co to jest _userManager?
NullVoxPopuli
6
W ASP.NET Core Identity Menedżer użytkowników to usługa świadczona przez Dependency Inject do tworzenia użytkowników. Więcej informacji można znaleźć w dokumentacji :
Ahmad,
2
Jak można to osiągnąć w metodzie innej niż asynchroniczna?
T3.0
Dla mnie zwraca zero. Czemu?
Alberto Cláudio Mandlate
22

Miej inny sposób na zdobycie bieżącego użytkownika w Asp.NET Core - i myślę, że widziałem to gdzieś tutaj, na 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;
}  

Ten kod trafia do kontrolera o nazwie DemoController. Nie będzie działać bez obu await (nie będzie się kompilować);)

Hekkaryk
źródło
Wymaga to użycia Identity
Fraze
1
Co to jest ApplicationUser?
Mike
ApplicationUser jest zwykle dziedziczony po IdentityUser, więc można go rozszerzyć o dodatkowe właściwości itp.
Corgalore
20

Muszę powiedzieć, że byłem dość zaskoczony, że HttpContext ma wartość null w konstruktorze. Jestem pewien, że to ze względu na wydajność. Potwierdziliśmy, że użycie w IPrincipalsposób opisany poniżej powoduje wstrzyknięcie go do konstruktora. Zasadniczo robi to samo, co zaakceptowana odpowiedź, ale w bardziej interfejsowy sposób.


Dla każdego, kto szuka tego pytania i szuka odpowiedzi na ogólne pytanie „Jak zdobyć aktualnego użytkownika?” możesz uzyskać Userbezpośredni dostęp z Controller.User. Ale możesz to zrobić tylko wewnątrz metod akcji (zakładam, że kontrolery działają nie tylko z HttpContexts i ze względu na wydajność).

Jeśli jednak potrzebujesz tego w konstruktorze (tak jak zrobił to OP) lub potrzebujesz stworzyć inne obiekty do wstrzyknięcia, które wymagają bieżącego użytkownika, poniższe jest lepszym podejściem:

Wstrzyknij IPrincipal, aby pobrać użytkownika

Najpierw spotkaj się IPrincipaliIIdentity

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

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

IPrincipali IIdentityreprezentuje użytkownika i nazwę użytkownika. Wikipedia pocieszy Cię, jeśli hasło „Principal” brzmi dziwnie .

Ważne jest, aby uświadomić sobie, że to, czy je dostać od IHttpContextAccessor.HttpContext.User, ControllerBase.Userczy ControllerBase.HttpContext.Userjesteś coraz obiekt, który jest gwarantowany być ClaimsPrincipalobiekt, który implementujeIPrincipal .

W tej chwili nie ma innego typu użytkownika, którego ASP.NET używa User(ale to nie znaczy, że inny nie mógłby zaimplementować czegoś innego IPrincipal).

Więc jeśli masz coś, co ma zależność od `` aktualnej nazwy użytkownika '', którą chcesz wstrzyknąć, powinieneś wstrzykiwać, IPrincipala na pewno nie IHttpContextAccessor.

Ważne: nie trać czasu na wstrzykiwanie IPrincipalbezpośrednio do kontrolera lub metody akcji - jest to bezcelowe, ponieważ Userjest już tam dostępne.

W startup.cs:

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

Następnie w twoim obiekcie DI, który potrzebuje użytkownika, którego po prostu wstrzykujesz, IPrincipalaby pobrać bieżącego użytkownika.

Najważniejsze jest to, że jeśli robisz testy jednostkowe, nie musisz wysyłać HttpContext, a jedynie kpić z czegoś, IPrincipal co reprezentuje to, co może byćClaimsPrincipal .

Jedna bardzo ważna rzecz, której nie jestem pewien w 100%. Jeśli chcesz uzyskać dostęp do rzeczywistych roszczeń ClaimsPrincipal, musisz przesłać IPrincipaldo ClaimsPrincipal. To jest w porządku, ponieważ wiemy w 100%, że w czasie wykonywania jest tego typu (ponieważ tak HttpContext.Userjest). Właściwie lubię to po prostu robić w konstruktorze, ponieważ już wiem na pewno, że każdy IPrincipal będzie ClaimsPrincipal.

Jeśli robisz kpiny, po prostu utwórz ClaimsPrincipalbezpośrednio i przekaż to do dowolnego ujęcia IPrincipal.

Dokładnie dlaczego nie ma interfejsu, bo IClaimsPrincipalnie jestem pewien. Zakładam, że MS zdecydowało, że ClaimsPrincipalto tylko wyspecjalizowana „kolekcja”, która nie gwarantuje interfejsu.

Simon_Weaver
źródło
2
Pozwala to na umieszczenie bieżącego użytkownika w dowolnym miejscu aplikacji - świetna odpowiedź!
Machado
1
To nie działa. Zawsze dostaję się nullza zastrzyk IPrincipal. Musiałem również dodać usługę przejściową jako …GetService<IHttpContextAccessor>()?.HttpContext.User…(z ?), ponieważ w przeciwnym razie uległaby awarii (GetService zwrócił wartość null).
ygoe
Możesz po prostu zrobić tak, services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);jak HttpContext.User jest ClaimsPrincipal.
Jay Zelos
18

Wydawałoby się, że na dzień dzisiejszy (kwiecień 2017 r.) Działa:

public string LoggedInUser => User.Identity.Name;

Przynajmniej w ciągu Controller

Grandizer
źródło
4
Nie można niejawnie przekonwertować typu „System.Security.Principal.IIdentity” na „ciąg”.
Anthony Huang
3
string LoggedInUser = User.Identity.Name;
Alic W
5
Jako ktoś, kto nie widział wcześniej takiego =>operatora, nazywa się to „definicją ciała wyrażenia” i jest opisane w tej dokumentacji . Na wypadek gdyby przyszli ludzie tacy jak ja się zastanawiali.
Nathan Clement
Twój kod nie mógł skompilować się przed edycją, biorąc pod uwagę, że nie ma konwersji z IIdentityna string, jak również stwierdzono w górnym komentarzu. Edycja po prostu to naprawiła. Nie jestem też pewien, jak doszedłeś do takiego wniosku (zwłaszcza, że ​​punkty "redaktora" są przyznawane tylko użytkownikom poniżej 2k reputacji).
awanturniczy
9

Być może nie widziałem odpowiedzi, ale tak to robię.

  1. .Net Core -> Właściwości -> launchSettings.json

Musisz zmienić te wartości

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

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Kontroler MVC lub Web Api

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

Metoda kontrolera:

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

Wynik to nazwa użytkownika, np. = Domena \ nazwa użytkownika

Tom Stickel
źródło
4

Mój problem polegał na uzyskaniu dostępu do zalogowanego użytkownika jako obiektu w pliku cshtml. Biorąc pod uwagę, że chciałeś użytkownika w ViewData, takie podejście może być pomocne:

W pliku 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>
MikeMajara
źródło
Dowolny pomysł, w jaki sposób można załadować właściwość nawigacji (nazwa firmy właściwości nawigacji firmy w mojej klasie ApplicationUser). Nie widziałem sposobu na uwzględnienie właściwości nawigacji.
Hunter Nelson
1

Oprócz istniejących odpowiedzi chciałbym dodać, że możesz również mieć instancję klasy dostępną dla całej aplikacji, która przechowuje dane związane z użytkownikiem, takie jak UserIDitp.

Może się przydać do refaktoryzacji np. nie chcesz pobierać UserIDw każdej akcji kontrolera i deklarować dodatkowy UserIDparametr w każdej metodzie związanej z warstwą usług.

Zrobiłem badania i oto mój post .

Po prostu rozszerzasz swoją klasę, z której wywodzisz DbContextsię, dodając UserIdwłaściwość (lub implementując niestandardowy plikSession klasę która ma tę właściwość).

Na poziomie filtra możesz pobrać instancję klasy i ustawić UserId wartość.

Następnie, gdziekolwiek wstrzykniesz swoją instancję - będzie ona zawierała niezbędne dane (czas życia musi wynosić na żądanie , więc rejestrujesz ją za pomocąAddScoped metodą).

Przykład pracy:

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

Aby uzyskać więcej informacji, zobacz moją odpowiedź .

Alex Herman
źródło
0

Zabieranie IdentityUserteż by działało. To jest bieżący obiekt użytkownika i można pobrać wszystkie wartości użytkownika.

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

var user = await _userManager.GetUserAsync(HttpContext.User);
zzdhxu
źródło
0

Jeśli używasz scafolded Identity i Asp.net Core 2.2+, możesz uzyskać dostęp do bieżącego użytkownika z widoku takiego:

@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

Andrzej
źródło
0

To stare pytanie, ale mój przypadek pokazuje, że mój przypadek nie był tutaj omawiany.

Najbardziej podoba mi się odpowiedź Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Wyjaśnia szczegółowo, jak uzyskać nazwę użytkownika za pomocą IPrincipal i IIdentity. Ta odpowiedź jest absolutnie poprawna i polecam takie podejście. Jednak podczas debugowania napotkałem problem, gdy ASP.NET NIE może poprawnie wypełnić zasady usługi . (lub innymi słowy, IPrincipal.Identity.Name ma wartość null)

Jest oczywiste, że aby uzyskać nazwę użytkownika, framework MVC powinien skądś wziąć to. W świecie .NET ASP.NET lub ASP.NET Core korzysta z oprogramowania pośredniego Open ID Connect. W prostym scenariuszu aplikacje internetowe uwierzytelniają użytkownika w przeglądarce internetowej. W tym scenariuszu aplikacja internetowa kieruje przeglądarkę użytkownika w celu zalogowania się do usługi Azure AD. Usługa Azure AD zwraca odpowiedź logowania za pośrednictwem przeglądarki użytkownika, która zawiera oświadczenia dotyczące użytkownika w tokenie zabezpieczającym. Aby działało w kodzie aplikacji, musisz podać uprawnienia, do których logują się delegaci aplikacji sieci Web. Podczas wdrażania aplikacji internetowej w usłudze Azure typowym scenariuszem spełniającym te wymagania jest skonfigurowanie aplikacji internetowej: „App Services” -> YourApp -> blok „Authentication / Authorization” -> „App Service Authenticatio” = „On”https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Wierzę (to jest moje wyuczone przypuszczenie), że pod maską tego procesu kreator dostosowuje „nadrzędną” konfigurację internetową tej aplikacji internetowej, dodając te same ustawienia, które pokazuję w kolejnych akapitach. Zasadniczo problem, dlaczego to podejście NIE działa w ASP.NET Core, polega na tym, że konfiguracja maszyny „nadrzędnej” jest ignorowana przez konfigurację webconfig. (nie jest to w 100% pewne, po prostu podaję najlepsze wyjaśnienie, jakie mam). Tak więc, aby wszystko działało, musisz ustawić to ręcznie w swojej aplikacji.

Oto artykuł wyjaśniający, jak wielokrotnie skonfigurować aplikację do korzystania z usługi Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Krok 1: Zarejestruj przykład w dzierżawie usługi Azure AD. (to oczywiste, nie chcę spędzać czasu na wyjaśnieniach).

Krok 2: W pliku appsettings.json: Zastąp wartość ClientID identyfikatorem aplikacji z aplikacji zarejestrowanej w portalu rejestracji aplikacji w kroku 1. Zastąp wartość TenantId typową

Krok 3: Otwórz plik Startup.cs i w metodzie ConfigureServices po wierszu zawierającym .AddAzureAD wstaw następujący kod, który umożliwia aplikacji logowanie użytkowników za pomocą punktu końcowego usługi Azure AD v2.0, czyli pracy i szkoły oraz Konta osobiste Microsoft.

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

Podsumowanie : Pokazałem jeszcze jeden możliwy problem, który może prowadzić do błędu, że jest wyjaśniony starter tematu. Przyczyną tego problemu jest brak konfiguracji usługi Azure AD (oprogramowanie pośredniczące Open ID). W celu rozwiązania tego problemu proponuję ręcznie ustawić „Uwierzytelnianie / Autoryzacja”. Dodano krótkie omówienie sposobu konfiguracji.

Roman Oira-Oira
źródło
0

Większość odpowiedzi pokazuje, jak najlepiej radzić sobie HttpContextz dokumentacją, z którą również poszedłem.

Chciałem wspomnieć, że podczas debugowania będziesz chciał sprawdzić ustawienia projektu, wartość domyślna to Enable Anonymous Authentication = true.

Juan Emmanuel Afable
źródło
-1

Mam swoje rozwiązanie

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
źródło
1
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Alexander