Como obter usuário atual no núcleo do asp.net

128

Desejo obter um usuário atual para obter informações de um usuário, como um email. Mas não posso fazer isso no núcleo do asp.net. Estou tão confuso Este é o meu código.

HttpContextquase é nulo no construtor do controlador. Não é bom ter um usuário em cada ação. Quero obter informações do usuário uma vez e configurá-las ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Mehran Hafizi
fonte
5
Usando com MVC ou Web APi?
Tushar #

Respostas:

172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT para construtor

O código abaixo funciona:

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

Editar para RTM

Você deve se registrar IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
adem caglin
fonte
2
funciona em actions.but eu quero usar no construtor do controlador.
Mehran Hafizi
3
é possível usar isso nas aulas?
Mehran Hafizi
5
ClaimTypes.NameIdentifierfornece o ID do usuário atual e ClaimTypes.Namefornece o nome de usuário.
Nikolay Kostov
3
Alguém pode me dizer o que há de errado com UserPrincipal.Current.Name?
tipura 23/03/18
2
@ademcaglin Por algumas razões, o usuário está retornando nullno meu caso? Estou usando .Net core 2.1 Web apiembora.
Sruthi Varghese
54

Maneira simples que funciona e eu verifiquei.

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

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

então você pode todas as propriedades dessas variáveis ​​como user.Email. Espero que isso ajude alguém.

Editar :

É uma coisa aparentemente simples, mas um pouco complicada, causa de diferentes tipos de sistemas de autenticação no ASP.NET Core. Eu atualizo porque algumas pessoas estão recebendo null.

Para autenticação JWT (Testado no 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
fonte
1
funciona muito bem para mim dentro de um controlador no asp.net Núcleo 2.0
jmdon
2
o que é um _userManager?
NullVoxPopuli
6
No ASP.NET Core Identity, o Gerenciador de usuários é um serviço fornecido pelo Dependency Inject para criar usuários. Consulte a documentação para obter mais informações:
Ahmad
2
Como isso pode ser alcançado em um método não assíncrono?
T3.0 2/04/19
Para mim está retornando nulo. Por quê?
Alberto Cláudio Mandlate
22

Tenho outra maneira de obter usuário atual no Asp.NET Core - e acho que vi em algum lugar aqui, no 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;
}  

Esse código vai para o controlador chamado DemoController. Não funcionará sem que ambos esperem (não será compilado);)

Hekkaryk
fonte
Isso requer o uso de identidade
Fraze
1
O que é o ApplicationUser?
1313 Mike
ApplicationUser é normalmente herdado do IdentityUser, para que possa ser estendido com propriedades adicionais, etc.
Corgalore
20

Devo dizer que fiquei bastante surpreso que o HttpContext seja nulo dentro do construtor. Tenho certeza que é por razões de desempenho. Confirmaram que o uso IPrincipalconforme descrito abaixo injeta no construtor. É essencialmente fazer o mesmo que a resposta aceita, mas de uma maneira mais interativa.


Para quem encontrar esta pergunta, procurando uma resposta para o genérico "Como obter usuário atual?" você pode simplesmente acessar Userdiretamente de Controller.User. Mas você só pode fazer isso dentro dos métodos de ação (presumo que os controladores não sejam executados apenas com HttpContexts e por razões de desempenho).

No entanto - se você precisar dele no construtor (como o OP fez) ou precisar criar outros objetos injetáveis ​​que precisam do usuário atual, a seguir é apresentada uma abordagem melhor:

Injete o IPrincipal para obter o usuário

Primeiro encontro IPrincipaleIIdentity

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

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

IPrincipale IIdentityrepresenta o usuário e o nome de usuário. A Wikipedia o confortará se 'Principal' parecer estranho .

Importante perceber que se você começar a partir de IHttpContextAccessor.HttpContext.User, ControllerBase.Userou ControllerBase.HttpContext.Uservocê está recebendo um objeto que está garantido para ser um ClaimsPrincipalobjeto que implementaIPrincipal .

Não há outro tipo de usuário para o qual o ASP.NET use Useragora (mas isso não quer dizer que outra coisa não possa ser implementada IPrincipal).

Portanto, se você tem algo que depende do 'nome do usuário atual' que deseja injetar, deve injetar IPrincipale definitivamente não IHttpContextAccessor.

Importante: Não perca tempo injetando IPrincipaldiretamente no seu controlador ou método de ação - é inútil, pois já Userestá disponível para você.

Em startup.cs:

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

Em seguida, no seu objeto DI que precisa do usuário, você apenas injeta IPrincipalpara obter o usuário atual.

A coisa mais importante aqui é que, se você estiver fazendo testes de unidade, não precisará enviar um HttpContext, mas precisará simular algo que represente o IPrincipal que pode ser ClaimsPrincipal .

Uma coisa extra importante que eu não tenho 100% de certeza. Se você precisar acessar as reivindicações reais, ClaimsPrincipalprecisará converter IPrincipalpara ClaimsPrincipal. Isso é bom, pois sabemos 100% que, em tempo de execução, é desse tipo (já que é isso que HttpContext.Useré). Na verdade, gosto de fazer isso no construtor, pois já tenho certeza de que IPrincipal será um ClaimsPrincipal.

Se você estiver fazendo zombaria, basta criar um ClaimsPrincipaldiretamente e passá-lo para o que for preciso IPrincipal.

Exatamente por que não há interface, IClaimsPrincipalnão tenho certeza. Presumo que a Microsoft tenha decidido que ClaimsPrincipalera apenas uma "coleção" especializada que não justificava uma interface.

Simon_Weaver
fonte
2
Isso permite que você injete o usuário atual em qualquer lugar do seu aplicativo, ótima resposta!
Machado
1
Isso não funciona. Eu sempre pego nullo injetado IPrincipal. Eu também precisava adicionar o serviço transitório como …GetService<IHttpContextAccessor>()?.HttpContext.User…(com o ?) porque ele falharia caso contrário (GetService retornou nulo).
ygoe 3/05/19
Você pode apenas fazer services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);como HttpContext.User é um ClaimsPrincipal.
Jay Zelos
18

Parece que, a partir de agora (abril de 2017), o seguinte funciona:

public string LoggedInUser => User.Identity.Name;

Pelo menos enquanto estiver dentro de um Controller

Grandizer
fonte
4
Você não pode converter implicitamente o tipo 'System.Security.Principal.IIdentity' em 'string'.
Anthony Huang
3
string LoggedInUser = User.Identity.Name;
Alic W
5
Como alguém que nunca viu o =>operador usar dessa maneira antes, é chamado de "Definição do corpo da expressão" e é descrito nesta documentação . Apenas no caso de futuras pessoas como eu estarem se perguntando.
22418 Nathan Clement
Seu código não pôde ser compilado antes da edição, já que não há conversão de IIdentitypara string, como também declarado no comentário principal. A edição simplesmente corrigiu isso. Também não tenho certeza de como você chegou à sua conclusão (principalmente porque os pontos de "editor" são dados apenas a usuários com menos de 2k de reputação).
fuglede 27/01
9

Talvez eu não tenha visto a resposta, mas é assim que faço.

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

Você precisa alterar esses valores

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

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Controlador MVC ou API da Web

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

Método do controlador:

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

O resultado é userName, por exemplo, = Domain \ username

Tom Stickel
fonte
4

Meu problema foi acessar o usuário conectado como um objeto no arquivo cshtml. Considerando que você queria o usuário no ViewData, essa abordagem pode ser útil:

No arquivo 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
fonte
Alguma idéia de como você pode carregar uma propriedade de navegação (nome da empresa de uma propriedade de navegação da empresa na minha classe ApplicationUser). Não encontrou uma maneira de incluir propriedades de navegação.
Hunter Nelson
1

Além das respostas existentes, gostaria de acrescentar que você também pode ter uma instância de classe disponível em todo o aplicativo que contém dados relacionados ao usuário, UserIDetc.

Pode ser útil para refatoração, p. você não deseja buscar UserIDtodas as ações do controlador e declarar um UserIDparâmetro extra em todos os métodos relacionados à Camada de Serviço.

Eu fiz uma pesquisa e aqui está o meu post .

Você apenas estende sua classe da qual deriva DbContextadicionando UserIdpropriedade (ou implementa uma Sessionclasse personalizada que possui essa propriedade).

No nível do filtro, você pode buscar sua instância de classe e definir UserIdvalor.

Depois disso, onde quer que você injete sua instância - ela terá os dados necessários (a vida útil deve ser por solicitação , para que você a registre usando o AddScopedmétodo).

Exemplo de trabalho:

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 mais informações, veja minha resposta .

Alex Herman
fonte
0

Tomar IdentityUsertambém funcionaria. Este é um objeto de usuário atual e todos os valores do usuário podem ser recuperados.

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

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

Se você estiver usando a Identidade do andaime e usando o Asp.net Core 2.2+, poderá acessar o usuário atual em uma exibição 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

Andrew
fonte
0

Esta é uma pergunta antiga, mas meu caso mostra que meu caso não foi discutido aqui.

Eu gosto mais da resposta de Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Ele explica em detalhes como obter o nome de usuário usando IPrincipal e IIdentity. Esta resposta está absolutamente correta e eu recomendo usar essa abordagem. No entanto, durante a depuração, encontrei o problema quando o ASP.NET NÃO pode preencher corretamente o princípio do serviço . (ou em outras palavras, IPrincipal.Identity.Name é nulo)

É óbvio que, para obter o nome de usuário, a estrutura MVC deve levá-la de algum lugar. No mundo .NET, o ASP.NET ou ASP.NET Core está usando o middleware Open ID Connect. No cenário simples, os aplicativos da web autenticam um usuário em um navegador da web. Nesse cenário, o aplicativo Web instrui o navegador do usuário a entrar no Azure AD. O Azure AD retorna uma resposta de entrada através do navegador do usuário, que contém declarações sobre o usuário em um token de segurança. Para que ele funcione no código do seu aplicativo, você precisará fornecer a autoridade na qual o aplicativo da Web delega a entrada. Quando você implanta seu aplicativo Web no Serviço do Azure, o cenário comum para atender a esses requisitos é configurar o aplicativo Web: "Serviços de Aplicativo" -> YourApp -> folha "Autenticação / Autorização" -> "Autenticação do Serviço de Aplicativo" = "Ativado"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Acredito (este é o meu palpite) que, sob o capô deste processo, o assistente ajusta a configuração da Web "pai" desse aplicativo da Web adicionando as mesmas configurações que mostro nos parágrafos seguintes. Basicamente, o problema por que essa abordagem NÃO funciona no ASP.NET Core é porque a configuração da máquina "pai" é ignorada pelo webconfig. (isso não é 100% certo, eu apenas dou a melhor explicação que tenho). Portanto, para que eu funcione, é necessário configurá-lo manualmente no seu aplicativo.

Aqui está um artigo que explica como configurar seu aplicativo de maneira geral para usar o Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Etapa 1: registre a amostra com seu inquilino do Azure AD. (é óbvio, não quero gastar meu tempo de explicações).

Etapa 2: no arquivo appsettings.json: substitua o valor ClientID pelo ID do aplicativo que você registrou no portal de registro de aplicativos na Etapa 1. substitua o valor TenantId por comum

Etapa 3: abra o arquivo Startup.cs e, no método ConfigureServices, após a linha que contém .AddAzureAD, insira o código a seguir, que permite ao seu aplicativo entrar usuários com o ponto de extremidade do Azure AD v2.0, que é Trabalho e Escola e Contas pessoais da Microsoft.

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

Resumo : mostrei mais um possível problema que pode estar relacionado a um erro que explica o iniciador de tópicos. O motivo desse problema está ausente nas configurações do Azure AD (middleware Open ID). Para resolver esse problema, proponho a configuração manual "Autenticação / Autorização". A breve visão geral de como configurar isso é adicionada.

Roman Oira-Oira
fonte
0

A maioria das respostas mostra como lidar melhor com HttpContexta documentação, que também foi com a qual eu fui.

Eu queria mencionar que você deseja verificar as configurações do projeto ao depurar, o padrão é Enable Anonymous Authentication = true.

Juan Emmanuel Afable
fonte
-1

Eu tenho minha solução

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
fonte
1
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e por que resolve o problema melhoraria o valor a longo prazo da resposta.
1018 Alexander Alexander