Cómo obtener el usuario actual en asp.net core


128

Quiero obtener un usuario actual para obtener información de un usuario, como un correo electrónico. Pero no puedo hacer eso en asp.net core. Estoy tan confundido. Este es mi código.

HttpContextCasi es nulo en el constructor del controlador. No es bueno tener un usuario en cada acción. Quiero obtener información del usuario una vez y configurarla en ViewData;

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

55
¿Utilizando con MVC o Web APi?
Tushar

Respuestas:


172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDITAR para constructor

El siguiente código funciona:

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

Editar para RTM

Debes registrarte IHttpContextAccessor:

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

2
funciona en acciones. pero quiero usarlo en el constructor del controlador.
Mehran Hafizi

3
¿Es posible usar esto en las clases?
Mehran Hafizi

55
ClaimTypes.NameIdentifierda la identificación de usuario actual y ClaimTypes.Nameda el nombre de usuario.
Nikolay Kostov

3
¿Alguien puede decirme qué está mal con UserPrincipal.Current.Name?
tipura

2
@ademcaglin Por alguna razón, ¿el usuario regresa nullen mi caso? .Net core 2.1 Web apiAunque estoy usando .
Sruthi Varghese

54

Manera simple que funciona y lo comprobé.

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

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

entonces puede todas las propiedades de estas variables como user.Email. Espero que esto ayude a alguien.

Editar :

Es una cosa aparentemente simple pero un poco complicada porque hay diferentes tipos de sistemas de autenticación en ASP.NET Core. Actualizo porque algunas personas están recibiendo null.

Para la autenticación JWT (probado en 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
funciona muy bien para mí dentro de un controlador en asp.net Core 2.0
jmdon

2
¿Qué es un _userManager?
NullVoxPopuli

66
En ASP.NET Core Identity, User Manager es un servicio provisto por Dependency Inject to Create Users. Consulte los documentos para obtener más información:
Ahmad

2
¿Cómo se puede lograr esto en un método no asíncrono?
T3.0

Para mí está volviendo nulo. ¿Por qué?
Alberto Cláudio Mandlate

22

Tengo otra forma de obtener el usuario actual en Asp.NET Core, y creo que lo vi en algún lugar aquí, en 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;
}  

Ese código va al controlador llamado DemoController. No funcionará sin esperar ambos (no se compilará);)


Esto requiere el uso de Identidad
Fraze

1
¿Qué es ApplicationUser?
Mike

ApplicationUser generalmente se hereda de IdentityUser, por lo que puede ampliarse con propiedades adicionales, etc.
Corgalore

20

Tengo que decir que me sorprendió bastante que HttpContext sea nulo dentro del constructor. Estoy seguro de que es por razones de rendimiento. Han confirmado que el uso IPrincipalcomo se describe a continuación lo inyecta en el constructor. Básicamente está haciendo lo mismo que la respuesta aceptada, pero de una manera más interactiva.


Para cualquiera que encuentre esta pregunta y busque una respuesta al genérico "¿Cómo obtener un usuario actual?" puedes acceder Userdirectamente desde Controller.User. Pero solo puede hacer esto dentro de los métodos de acción (supongo que los controladores no solo se ejecutan con HttpContexts y por razones de rendimiento).

Sin embargo, si lo necesita en el constructor (como lo hizo OP) o necesita crear otros objetos inyectables que necesitan el usuario actual, entonces el siguiente es un mejor enfoque:

Inyecte IPrincipal para obtener el usuario

Primer encuentro IPrincipalyIIdentity

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

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

IPrincipaly IIdentityrepresenta al usuario y nombre de usuario. Wikipedia te consolará si 'Principal' suena extraño .

Es importante darse cuenta de que si la recibe de IHttpContextAccessor.HttpContext.User, ControllerBase.Usero ControllerBase.HttpContext.Userque está recibiendo un objeto que se garantiza que sea un ClaimsPrincipalobjeto que implementaIPrincipal .

No hay otro tipo de usuario que ASP.NET use en Usereste momento (pero eso no quiere decir que otra cosa no pueda implementar IPrincipal).

Entonces, si tiene algo que depende del 'nombre de usuario actual' que desea inyectar, debería inyectar IPrincipaly definitivamente no IHttpContextAccessor.

Importante: No pierda el tiempo inyectando IPrincipaldirectamente en su controlador o método de acción, no tiene sentido ya Userque ya está disponible para usted.

En startup.cs:

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

Luego, en su objeto DI que necesita el usuario que acaba de inyectar IPrincipalpara obtener el usuario actual.

Lo más importante aquí es que si está haciendo pruebas unitarias, no necesita enviar una HttpContext, pero solo necesita burlarse de algo que representa lo IPrincipal que puede ser ClaimsPrincipal .

Una cosa extra importante de la que no estoy 100% seguro. Si necesita acceder a los reclamos reales ClaimsPrincipal, debe enviarlos IPrincipala ClaimsPrincipal. Esto está bien ya que sabemos al 100% que en tiempo de ejecución es de ese tipo (ya que eso HttpContext.Useres lo que es). De hecho, me gusta hacer esto en el constructor, ya que sé con certeza que cualquiera IPrincipal será a ClaimsPrincipal.

Si te estás burlando, solo crea uno ClaimsPrincipaldirectamente y pásalo a lo que sea necesario IPrincipal.

Exactamente por qué no hay interfaz porque IClaimsPrincipalno estoy seguro. Supongo que MS decidió que ClaimsPrincipalera solo una 'colección' especializada que no garantizaba una interfaz.


2
Esto le permite inyectar al usuario actual en cualquier lugar de su aplicación, ¡gran respuesta!
Machado

1
Esto no funciona Siempre me pongo nullpara el inyectado IPrincipal. También necesitaba agregar el servicio transitorio como …GetService<IHttpContextAccessor>()?.HttpContext.User…(con el ?) porque de lo contrario se bloquearía (GetService devuelve nulo).
ygoe

Simplemente puede hacer services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);como HttpContext.User es un ClaimsPrincipal.
Jay Zelos

18

Parece que a partir de ahora (abril de 2017) funciona lo siguiente:

public string LoggedInUser => User.Identity.Name;

Al menos dentro de un Controller


44
No puede convertir implícitamente el tipo 'System.Security.Principal.IIdentity' a 'string'.
Anthony Huang

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

55
Como alguien que no había visto el =>operador utilizado así antes, se llama "Definición del cuerpo de expresión" y se describe en esta documentación . Por si acaso la gente como yo se preguntaba.
Nathan Clement

Su código no podría compilarse antes de la edición, dado que no hay conversión de IIdentitya string, como también se indica en el comentario superior. La edición simplemente solucionó eso. Tampoco estoy seguro de cómo llegó a su conclusión (en particular, ya que los puntos de "editor" se otorgan solo a usuarios con una reputación inferior a 2k).
fuglede

9

Quizás no vi la respuesta, pero así es como lo hago.

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

Necesita cambiar estos valores

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

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC o controlador de API web

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

Método del controlador:

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

El resultado es userName, por ejemplo, = dominio \ nombre de usuario


4

Mi problema era acceder al Usuario conectado como un objeto en el archivo cshtml. Teniendo en cuenta que deseaba el usuario en ViewData, este enfoque podría ser útil:

En el archivo 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>

Alguna idea de cómo puede cargar una propiedad de navegación (nombre de la empresa de una propiedad de navegación de la empresa en mi clase ApplicationUser). No vi una manera de incluir propiedades de navegación.
Hunter Nelson el

1

Además de las respuestas existentes, me gustaría agregar que también puede tener una instancia de clase disponible en toda la aplicación que contiene datos relacionados con el usuario como UserIDetc.

Puede ser útil para refactorizar, p. no desea buscar UserIDen cada acción del controlador y declarar un UserIDparámetro adicional en cada método relacionado con la capa de servicio.

He hecho una investigación y aquí está mi publicación .

Simplemente extienda su clase de la que deriva DbContextagregando UserIdpropiedad (o implemente una Sessionclase personalizada que tenga esta propiedad).

A nivel de filtro, puede buscar su instancia de clase y establecer el UserIdvalor.

Después de eso, donde sea que inyecte su instancia, tendrá los datos necesarios (la vida útil debe ser por solicitud , por lo que debe registrarla utilizando el AddScopedmétodo).

Ejemplo de trabajo:

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

Para más información mira mi respuesta .


0

Tomar IdentityUsertambién funcionaría. Este es un objeto de usuario actual y se pueden recuperar todos los valores de usuario.

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

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

0

Si está utilizando la identidad escaneada y está utilizando Asp.net Core 2.2+, puede acceder al usuario actual desde una vista como esta:

@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

Esta es una pregunta antigua, pero mi caso muestra que mi caso no se discutió aquí.

Me gusta más la respuesta de Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Explica en detalle cómo obtener el nombre de usuario utilizando IPrincipal e IIdentity. Esta respuesta es absolutamente correcta y recomiendo usar este enfoque. Sin embargo, durante la depuración me encontré con el problema cuando ASP.NET NO puede completar correctamente el principio de servicio . (o en otras palabras, IPrincipal.Identity.Name es nulo)

Es obvio que para obtener el nombre de usuario MVC Framework debería tomarlo de alguna parte. En el mundo .NET, ASP.NET o ASP.NET Core está utilizando el middleware Open ID Connect. En el escenario simple, las aplicaciones web autentican a un usuario en un navegador web. En este escenario, la aplicación web dirige al navegador del usuario para que inicie sesión en Azure AD. Azure AD devuelve una respuesta de inicio de sesión a través del navegador del usuario, que contiene notificaciones sobre el usuario en un token de seguridad. Para que funcione en el código de su aplicación, deberá proporcionar la autoridad a la que sus aplicaciones web delegan el inicio de sesión. Cuando implementa su aplicación web en Azure Service, el escenario común para cumplir con estos requisitos es configurar la aplicación web: "App Services" -> YourApp -> "Authentication / Authorization" blade -> "App Service Authenticatio" = "On"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Creo (esta es mi suposición educada) que, bajo el capó de este proceso, el asistente ajusta la configuración web "principal" de esta aplicación web al agregar la misma configuración que muestro en los siguientes párrafos. Básicamente, el problema por el cual este enfoque NO funciona en ASP.NET Core es porque webconfig ignora la configuración de la máquina "principal". (Esto no es 100% seguro, solo doy la mejor explicación que tengo). Por lo tanto, para que funcione, debe configurarlo manualmente en su aplicación.

Aquí hay un artículo que explica cómo configurar tu aplicación de manera manual para usar Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Paso 1: registre la muestra con su inquilino de Azure AD. (Es obvio, no quiero pasar mi tiempo de explicaciones).

Paso 2: en el archivo appsettings.json: reemplace el valor de ClientID con el ID de la aplicación que registró en el portal de registro de aplicaciones en el Paso 1. reemplace el valor de TenantId con common

Paso 3: abra el archivo Startup.cs y en el método ConfigureServices, después de la línea que contiene .AddAzureAD inserte el siguiente código, que permite que su aplicación inicie sesión en los usuarios con el punto final de Azure AD v2.0, que es tanto Work como School y Cuentas personales de Microsoft.

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

Resumen : He mostrado un posible problema más que podría dar lugar a un error que explica el iniciador del tema. La razón de este problema son las configuraciones faltantes para Azure AD (middleware de ID abierta). Para resolver este problema, propongo configurar manualmente "Autenticación / Autorización". Se agrega la breve descripción general de cómo configurar esto.


0

La mayoría de las respuestas muestran cómo manejar mejor HttpContextla documentación, que también es lo que utilicé.

Quería mencionar que querrá verificar la configuración de su proyecto al depurar, el valor predeterminado es Enable Anonymous Authentication = true.


-1

Tengo mi solucion

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
Si bien este código puede responder la pregunta, proporcionar un contexto adicional sobre cómo y por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Alexander
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.