Comment obtenir l'utilisateur actuel dans le noyau asp.net

129

Je souhaite obtenir un utilisateur actuel pour obtenir des informations sur un utilisateur, comme un e-mail. Mais je ne peux pas faire cela dans le noyau asp.net. Je suis tellement confus C'est mon code.

HttpContextpresque est nul dans le constructeur du contrôleur. Il n'est pas bon d'avoir un utilisateur dans chaque action. Je veux obtenir des informations sur l'utilisateur une fois et les définir dans ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Mehran Hafizi
la source
5
Utilisation avec MVC ou Web APi?
Tushar

Réponses:

172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT pour le constructeur

Le code ci-dessous fonctionne:

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

Modifier pour RTM

Vous devez vous inscrire IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
adem caglin
la source
2
cela fonctionne dans les actions.Mais je veux utiliser dans le constructeur du contrôleur.
Mehran Hafizi
3
est-il possible de l'utiliser dans les classes?
Mehran Hafizi
5
ClaimTypes.NameIdentifierdonne l'ID utilisateur actuel et ClaimTypes.Namedonne le nom d'utilisateur.
Nikolay Kostov
3
Quelqu'un peut-il me dire ce qui ne va pas avec UserPrincipal.Current.Name?
tipura
2
@ademcaglin Pour certaines raisons L'utilisateur revient nulldans mon cas? J'utilise .Net core 2.1 Web apicependant.
Sruthi Varghese
55

Manière simple qui fonctionne et j'ai vérifié.

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

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

alors vous pouvez toutes les propriétés de ces variables comme user.Email. J'espère que cela aiderait quelqu'un.

Modifier :

C'est une chose apparemment simple mais un peu compliquée à cause des différents types de systèmes d'authentification dans ASP.NET Core. Je mets à jour parce que certaines personnes obtiennent null.

Pour l'authentification JWT (testée sur 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
la source
1
fonctionne très bien pour moi à l'intérieur d'un contrôleur dans asp.net Core 2.0
jmdon
2
qu'est-ce qu'un _userManager?
NullVoxPopuli
6
Dans ASP.NET Core Identity, User Manager est un service fourni par Dependency Inject pour créer des utilisateurs. Voir la documentation pour plus d'informations:
Ahmad
2
Comment cela peut-il être réalisé dans une méthode non asynchrone?
T3.0
Pour moi, revient nul. Pourquoi?
Alberto Cláudio Mandlate
22

J'ai un autre moyen d'obtenir l'utilisateur actuel dans Asp.NET Core - et je pense que je l'ai vu quelque part ici, sur 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;
}  

Ce code va au contrôleur nommé DemoController. Ne fonctionnera pas sans attendre les deux (ne compilera pas);)

Hekkaryk
la source
Cela nécessite l'utilisation de l'identité
Fraze
1
Qu'est-ce que ApplicationUser?
Mike
ApplicationUser est généralement hérité d'IdentityUser afin qu'il puisse être étendu avec des propriétés supplémentaires, etc.
Corgalore
20

Je dois dire que j'ai été assez surpris que HttpContext soit nul à l'intérieur du constructeur. Je suis sûr que c'est pour des raisons de performance. Avoir confirmé que l'utilisation IPrincipalcomme décrit ci-dessous l'injecte dans le constructeur. Il fait essentiellement la même chose que la réponse acceptée, mais d'une manière plus interface.


Pour tous ceux qui trouvent cette question à la recherche d'une réponse au générique "Comment obtenir l'utilisateur actuel?" vous pouvez simplement accéder Userdirectement à partir de Controller.User. Mais vous ne pouvez le faire qu'à l'intérieur des méthodes d'action (je suppose que les contrôleurs ne fonctionnent pas uniquement avec HttpContexts et pour des raisons de performances).

Cependant - si vous en avez besoin dans le constructeur (comme OP l'a fait) ou si vous devez créer d'autres objets injectables qui ont besoin de l'utilisateur actuel, alors la solution ci-dessous est une meilleure approche:

Injectez IPrincipal pour obtenir l'utilisateur

Première rencontre IPrincipaletIIdentity

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

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

IPrincipalet IIdentityreprésente l'utilisateur et le nom d'utilisateur. Wikipedia vous réconfortera si «Principal» semble étrange .

Il est important de se rendre compte que si vous l' obtenez à partir IHttpContextAccessor.HttpContext.User, ControllerBase.Userou ControllerBase.HttpContext.Uservous faire un objet qui est garanti d'être un ClaimsPrincipalobjet qui met en œuvreIPrincipal .

Il n'y a aucun autre type d'utilisateur utilisé par ASP.NET pour Userle moment (mais cela ne veut pas dire que quelque chose d'autre ne pourrait pas être implémenté IPrincipal).

Donc, si vous avez quelque chose qui a une dépendance du «nom d'utilisateur actuel» que vous voulez injecter, vous devriez l'injecter IPrincipalet certainement pas IHttpContextAccessor.

Important: ne perdez pas de temps à injecter IPrincipaldirectement dans votre manette ou votre méthode d'action - c'est inutile car Uservous y êtes déjà disponible.

Dans startup.cs:

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

Ensuite, dans votre objet DI qui a besoin de l'utilisateur que vous venez d'injecter IPrincipalpour obtenir l'utilisateur actuel.

La chose la plus importante ici est que si vous faites des tests unitaires, vous n'avez pas besoin d'envoyer un HttpContext, mais seulement de vous moquer de quelque chose qui représente IPrincipal ce qui peut simplement être ClaimsPrincipal .

Une chose supplémentaire importante dont je ne suis pas sûr à 100%. Si vous avez besoin d'accéder aux demandes d' indemnités de ClaimsPrincipalvous devez convertir IPrincipalà ClaimsPrincipal. C'est très bien car nous savons à 100% qu'au moment de l'exécution, c'est de ce type (puisque c'est ce qui HttpContext.Userest). En fait, j'aime faire cela dans le constructeur car je sais déjà avec certitude que tout IPrincipal sera un fichier ClaimsPrincipal.

Si vous vous moquez, créezClaimsPrincipal simplement un directement et transmettez-le à tout ce qui prend IPrincipal.

Exactement pourquoi il n'y a pas d'interface pour IClaimsPrincipalje ne suis pas sûr. Je suppose que MS a décidé que ce ClaimsPrincipaln'était qu'une «collection» spécialisée qui ne justifiait pas une interface.

Simon_Weaver
la source
2
Cela vous permet d'injecter l'utilisateur actuel n'importe où dans votre application, excellente réponse!
Machado
1
Cela ne marche pas. Je reçois toujours nullpour l'injection IPrincipal. J'avais également besoin d'ajouter le service transitoire en tant que …GetService<IHttpContextAccessor>()?.HttpContext.User…(avec le ?) car il planterait sinon (GetService retournait null).
ygoe
Vous pouvez simplement faire services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);comme HttpContext.User est un ClaimsPrincipal.
Jay Zelos le
18

Il semblerait qu'à partir de maintenant (avril 2017), ce qui suit fonctionne:

public string LoggedInUser => User.Identity.Name;

Au moins dans un Controller

Grandizer
la source
4
Vous ne pouvez pas convertir implicitement le type «System.Security.Principal.IIdentity» en «chaîne».
Anthony Huang
3
string LoggedInUser = User.Identity.Name;
Alic W
5
En tant que personne qui n'avait jamais vu l' =>opérateur utilisé comme ça auparavant, cela s'appelle une "Définition du corps d'expression" et est décrit dans cette documentation . Juste au cas où de futurs gens comme moi se demanderaient.
Nathan Clement
Votre code n'a pas pu être compilé avant la modification, étant donné qu'il n'y a pas de conversion de IIdentityen string, comme indiqué également dans le commentaire supérieur. La modification a simplement corrigé cela. Je ne sais pas non plus comment vous êtes parvenu à votre conclusion (d'autant plus que les points "éditeur" ne sont attribués qu'aux utilisateurs ayant une réputation inférieure à 2k).
fuglede le
9

Peut-être que je n'ai pas vu la réponse, mais c'est ainsi que je le fais.

  1. .Net Core -> Propriétés -> launchSettings.json

Vous devez avoir changé ces valeurs

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

Startup.cs -> Configurer les services (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Contrôleur MVC ou Web Api

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

Méthode de contrôleur:

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

Le résultat est userName, par exemple = Domaine \ nom d'utilisateur

Tom Stickel
la source
4

Mon problème était d'accéder à l'utilisateur connecté en tant qu'objet dans le fichier cshtml. Considérant que vous vouliez l'utilisateur dans ViewData, cette approche peut être utile:

Dans le fichier 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
la source
Toute idée de la façon dont vous pourriez obtenir une propriété de navigation à charger (nom de société d'une propriété de navigation d'entreprise sur ma classe ApplicationUser). Je n'ai pas vu de moyen d'inclure les propriétés de navigation.
Hunter Nelson
1

En plus des réponses existantes, j'aimerais ajouter que vous pouvez également disposer d'une instance de classe disponible à l'échelle de l'application qui contient des données relatives à l'utilisateur, telles que UserIDetc.

Cela peut être utile pour refactoriser par ex. vous ne voulez pas récupérer UserIDdans chaque action du contrôleur et déclarer un UserIDparamètre supplémentaire dans chaque méthode liée à la couche de service.

J'ai fait une recherche et voici mon message .

Vous étendez simplement votre classe dont vous dérivez DbContexten ajoutant une UserIdpropriété (ou en implémentez une Sessionclasse personnalisée qui a cette propriété).

Au niveau du filtre, vous pouvez récupérer votre instance de classe et définir la UserIdvaleur.

Après cela, partout où vous injectez votre instance - elle aura les données nécessaires (la durée de vie doit être par requête , vous l'enregistrez donc à l'aide de AddScopedmethod).

Exemple de travail:

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

Pour plus d'informations, consultez ma réponse .

Alex Herman
la source
0

Prendre IdentityUserfonctionnerait également. Il s'agit d'un objet utilisateur actuel et toutes les valeurs de l'utilisateur peuvent être récupérées.

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

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

Si vous utilisez l' identité scafolded et que vous utilisez Asp.net Core 2.2+, vous pouvez accéder à l'utilisateur actuel à partir d'une vue comme celle-ci:

@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
la source
0

C'est une vieille question mais mon cas montre que mon cas n'a pas été discuté ici.

J'aime le plus la réponse de Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Il explique en détail comment obtenir le nom d'utilisateur en utilisant IPrincipal et IIdentity. Cette réponse est absolument correcte et je recommande d'utiliser cette approche. Cependant, pendant le débogage, j'ai rencontré le problème lorsque ASP.NET ne peut PAS remplir correctement le principe de service . (ou en d'autres termes, IPrincipal.Identity.Name est nul)

Il est évident que pour obtenir le nom d'utilisateur, le framework MVC doit le prendre quelque part. Dans le monde .NET, ASP.NET ou ASP.NET Core utilise le middleware Open ID Connect. Dans le scénario simple, les applications Web authentifient un utilisateur dans un navigateur Web. Dans ce scénario, l'application Web demande au navigateur de l'utilisateur de se connecter à Azure AD. Azure AD renvoie une réponse de connexion via le navigateur de l'utilisateur, qui contient des revendications sur l'utilisateur dans un jeton de sécurité. Pour que cela fonctionne dans le code de votre application, vous devez fournir l'autorité à laquelle vous déléguez la connexion de l'application Web. Lorsque vous déployez votre application Web sur Azure Service, le scénario courant pour répondre à ces exigences consiste à configurer l'application Web: "App Services" -> YourApp -> Blade "Authentication / Authorization" -> "App Service Authenticatio" = "On"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Je crois (c'est ma supposition éclairée) que sous le capot de ce processus, l'assistant ajuste la configuration Web "parent" de cette application Web en ajoutant les mêmes paramètres que ceux que je montre dans les paragraphes suivants. Fondamentalement, le problème pour lequel cette approche ne fonctionne PAS dans ASP.NET Core est que la configuration de la machine "parente" est ignorée par webconfig. (ce n'est pas sûr à 100%, je donne juste la meilleure explication que j'ai). Donc, pour que cela fonctionne, vous devez le configurer manuellement dans votre application.

Voici un article qui explique comment configurer plusieurs fois votre application pour utiliser Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Étape 1: inscrivez l'exemple auprès de votre locataire Azure AD. (c'est évident, je ne veux pas passer mon temps aux explications).

Étape 2: Dans le fichier appsettings.json: remplacez la valeur ClientID par l'ID d'application de l'application que vous avez enregistrée dans le portail d'inscription des applications à l'étape 1. remplacez la valeur TenantId par common

Étape 3: ouvrez le fichier Startup.cs et dans la méthode ConfigureServices, après la ligne contenant .AddAzureAD, insérez le code suivant, qui permet à votre application de connecter des utilisateurs avec le point de terminaison Azure AD v2.0, qui est à la fois professionnel et scolaire et Comptes personnels Microsoft.

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

Résumé : J'ai montré un autre problème possible qui pourrait entraîner une erreur expliquant le début du sujet. La raison de ce problème est l'absence de configurations pour Azure AD (middleware Open ID). Afin de résoudre ce problème, je propose de configurer manuellement "Authentification / Autorisation". Le bref aperçu de la façon de configurer ceci est ajouté.

Roman Oira-Oira
la source
0

La plupart des réponses montrent comment gérer au mieux HttpContextla documentation, ce que j'ai également choisi.

Je voulais mentionner que vous voudrez vérifier les paramètres de votre projet lors du débogage, la valeur par défaut est Enable Anonymous Authentication = true.

Juan Emmanuel Afable
la source
-1

J'ai ma solution

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
la source
1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la manière et la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
Alexander