Cómo obtener un usuario actual en asp.net core
Quiero obtener el usuario actual para poder acceder a campos como su dirección de correo electrónico. Pero no puedo hacer eso en el núcleo de asp.net. 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 la información del usuario una vez y guardarla en ViewData
;
public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
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
Deberías registrarte IHttpContextAccessor
:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
}
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 puedes conocer todas las propiedades de estas variables como user.Email
. Espero que esto ayude a alguien.
Editar :
Es algo aparentemente simple pero un poco complicado debido a los diferentes tipos de sistemas de autenticación en ASP.NET Core. Actualizo porque algunas personas lo están recibiendo null
.
Para 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);
Debo decir que me sorprendió bastante que HttpContext sea nulo dentro del constructor. Estoy seguro de que es por motivos de rendimiento. He confirmado que usar IPrincipal
como se describe a continuación lo inyecta en el constructor. Básicamente, hace lo mismo que la respuesta aceptada, pero de una manera más interfaz.
Para cualquiera que encuentre esta pregunta y busque una respuesta a la pregunta genérica "¿Cómo obtener un usuario actual?" puedes acceder User
directamente desde Controller.User
. Pero solo puedes hacer esto dentro de los métodos de acción (supongo que porque 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, el siguiente es un mejor enfoque:
Inyecte IPrincipal para obtener usuario
primer encuentro IPrincipal
yIIdentity
public interface IPrincipal
{
IIdentity Identity { get; }
bool IsInRole(string role);
}
public interface IIdentity
{
string AuthenticationType { get; }
bool IsAuthenticated { get; }
string Name { get; }
}
IPrincipal
y IIdentity
representa el usuario y el nombre de usuario. "Wikipedia lo consolará si 'Director' suena extraño ".
Es importante tener en cuenta que, ya sea que lo obtenga de IHttpContextAccessor.HttpContext.User
, ControllerBase.User
o ControllerBase.HttpContext.User
que obtenga un objeto, se garantiza que será un ClaimsPrincipal
objeto que implementeIPrincipal
.
No hay ningún otro tipo de usuario que ASP.NET utilice en User
este momento (pero eso no quiere decir que otra cosa no pueda implementarse IPrincipal
).
Entonces, si tiene algo que depende del 'nombre de usuario actual' que desea inyectar, debería inyectarlo 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 User
ya está disponible allí.
En startup.cs
:
// Inject IPrincipal
services.AddHttpContextAccessor();
services.AddTransient<IPrincipal>(
provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Luego, en su objeto DI que necesita el usuario, simplemente inyecta IPrincipal
para obtener el usuario actual.
Lo más importante aquí es que si estás haciendo pruebas unitarias, no necesitas enviar un HttpContext
, solo necesitas simular algo que represente IPrincipal
lo que puede ser simplemente ClaimsPrincipal
.
Una cosa extra importante de la que no estoy 100% seguro. Si necesita acceder a las reclamaciones reales, ClaimsPrincipal
debe enviarlas 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 lo que HttpContext.User
es). De hecho, me gusta hacer esto en el constructor porque ya sé con certeza que cualquiera IPrincipal
será un archivo ClaimsPrincipal
.
Si te estás burlando, simplemente crea un ClaimsPrincipal
directamente y pásalo a lo que sea necesario IPrincipal
.
IClaimsPrincipal
No estoy seguro exactamente por qué no hay una interfaz . Supongo que MS decidió que ClaimsPrincipal
era sólo una "colección" especializada que no justificaba una interfaz.