c# asp.net .net asp.net-core asp.net-identity

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



asp.net-core asp.net-identity (12)

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.

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


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 UserID usuario, etc.

Puede ser útil para refactorizar, por ejemplo, no desea obtener el UserID de UserID en cada acción del controlador y declarar un parámetro de UserID 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 que deriva de DbContext agregando la propiedad UserId (o implemente una clase de Session personalizada que tenga esta propiedad).

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

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 método AddScoped ).

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 .


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

Lo que más me gusta es la respuesta de Simon_Weaver ( https://.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" y así en ( 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 agregando 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 configurar esto manualmente en su aplicación.

Aquí hay un artículo que explica cómo configurar su 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.

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

Resumen : He mostrado un posible problema más que podría dar lugar a un error que explica el iniciador del tema. El motivo 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.


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 debido a los diferentes tipos de sistemas de autenticación en ASP.NET Core. Actualizo porque algunas personas se están volviendo 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);


Mi problema fue 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>


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

public string LoggedInUser => User.Identity;

Al menos dentro de un Controller


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


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


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


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á);)


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 usar IPrincipal como 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?" solo puede acceder al User directamente 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

Primero conozca IPrincipal e IIdentity

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

IPrincipal e IIdentity representa el usuario y el nombre de usuario. Wikipedia te consolará si ''Principal'' suena extraño .

Es importante darse cuenta de que si lo obtiene de IHttpContextAccessor.HttpContext.User , ControllerBase.User o ControllerBase.HttpContext.User , obtendrá un objeto que se garantiza que es un objeto ClaimsPrincipal que implementa IPrincipal .

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

Entonces, si tiene algo que tiene una dependencia del ''nombre de usuario actual'' que desea inyectar, debería inyectar IPrincipal y definitivamente no IHttpContextAccessor .

Importante: No pierda el tiempo inyectando IPrincipal directamente en su controlador o método de acción; no tiene sentido ya que el User ya está disponible para usted allí.

En startup.cs :

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

Luego, en su objeto DI que necesita al usuario, simplemente inyecte IPrincipal para obtener el usuario actual.

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

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

Si se está burlando, simplemente cree un ClaimsPrincipal directamente y páselo a lo que sea necesario.

Exactamente por qué no hay una interfaz para IClaimsPrincipal no estoy seguro. Supongo que MS decidió que ClaimsPrincipal era solo una ''colección'' especializada que no garantizaba una interfaz.


Tomar IdentityUser tambié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);


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

Debe registrar IHttpContextAccessor :

public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }