Programing

asp.net 코어에서 현재 사용자를 얻는 방법

crosscheck 2020. 9. 3. 07:33
반응형

asp.net 코어에서 현재 사용자를 얻는 방법


이메일과 같은 사용자의 정보를 얻기 위해 현재 사용자를 얻고 싶습니다. 하지만 asp.net 코어에서는 그렇게 할 수 없습니다. 나는 너무 혼란스러워 이것은 내 코드입니다.

HttpContext컨트롤러 생성자 에서 거의 null입니다 . 각 작업에서 사용자를 확보하는 것은 좋지 않습니다. 사용자의 정보를 한 번 얻고 다음과 같이 설정하고 싶습니다 ViewData.

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

User.FindFirst(ClaimTypes.NameIdentifier).Value

생성자 편집

아래 코드가 작동합니다.

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

RTM 편집

등록해야합니다 IHttpContextAccessor:

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

작동하는 간단한 방법과 나는 확인했습니다.

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

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

그런 다음이 변수의 모든 속성을 user.Email. 나는 이것이 누군가를 도울 수 있기를 바랍니다.

편집 :

ASP.NET Core에서 다양한 유형의 인증 시스템을 사용하는 것은 분명히 간단하지만 약간 복잡한 원인입니다. 나는 일부 사람들이 받고 있기 때문에 업데이트 null합니다.

JWT 인증의 경우 (ASP.NET Core v3.0.0-preview7에서 테스트 됨) :

var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

var user = await _userManager.FindByEmailAsync(email);

Asp.NET Core에서 현재 사용자를 얻는 다른 방법이 있습니다-여기 어딘가에서 본 것 같습니다 ^^

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

이 코드는 DemoController라는 컨트롤러로 이동합니다. 둘 다 기다리지 않으면 작동하지 않습니다 (컴파일하지 않음);)


현재 (2017 년 4 월) 현재 다음 사항이 작동하는 것으로 보입니다.

public string LoggedInUser => User.Identity;

적어도 Controller


HttpContext가 생성자 내부에서 null이라는 사실에 꽤 놀랐습니다. 나는 그것이 성능상의 이유라고 확신합니다. IPrincipal아래 설명 된대로 사용 하면 생성자에 주입 되는지 확인했습니다 . 본질적으로 받아 들여지는 대답과 동일하지만 더 인터페이스 방식으로 수행됩니다.


이 질문을 찾는 사람은 일반적인 "현재 사용자를 얻는 방법" 에 대한 답변을 찾고 있습니다. User에서 직접 액세스 할 수 있습니다 Controller.User. 그러나이 작업은 액션 메서드 내에서만 수행 할 수 있습니다 (컨트롤러가 HttpContexts 및 성능상의 이유로 만 실행되지 않기 때문에 가정합니다).

그러나-생성자에서 필요하거나 (OP처럼) 현재 사용자가 필요한 다른 주입 가능한 개체를 만들어야하는 경우 아래 방법이 더 나은 방법입니다.

IPrincipal을 삽입하여 사용자 가져 오기

먼저 충족 IPrincipalIIdentity

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

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

IPrincipalIIdentity사용자와 사용자 이름을 나타냅니다. Wikipedia는 'Principal'이 이상하게 들리면 위로 할 것 입니다.

중요 실현하기 위해 당신이 그것을에서 얻을 여부 IHttpContextAccessor.HttpContext.User, ControllerBase.User또는 ControllerBase.HttpContext.User당신이있어 이 보장되는 객체 받고 ClaimsPrincipal있는 구현 객체를IPrincipal .

현재 ASP.NET이 사용하는 다른 유형의 사용자는 User없습니다 (그러나 다른 것이 구현할 수 없다는 것은 아닙니다 IPrincipal).

그래서 당신은 당신이 당신이 주입되어야 주입하려는 '현재 사용자 이름'의 종속성 무언가가 있다면 IPrincipal확실히하지 IHttpContextAccessor.

중요 :IPrincipal 컨트롤러 또는 작업 방법에 직접 주입 하는 데 시간을 낭비하지 마십시오 User. 이미 거기에서 사용할 수 있으므로 의미 가 없습니다.

에서 startup.cs:

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

Then in your DI object that needs the user you just inject IPrincipal to get the current user.

The most important thing here is if you're doing unit tests you don't need to send in an HttpContext, but only need to mock something that represents IPrincipal which can just be ClaimsPrincipal.

One extra important thing that I'm not 100% sure about. If you need to access the actual claims from ClaimsPrincipal you need to cast IPrincipal to ClaimsPrincipal. This is fine since we know 100% that at runtime it's of that type (since that's what HttpContext.User is). I actually like to just do this in the constructor since I already know for sure any IPrincipal will be a ClaimsPrincipal.

If you're doing mocking, just create a ClaimsPrincipal directly and pass it to whatever takes IPrincipal.

Exactly why there is no interface for IClaimsPrincipal I'm not sure. I assume MS decided that ClaimsPrincipal was just a specialized 'collection' that didn't warrant an interface.


My problem was to access the logged in User as an object in the cshtml file. Considering you wanted the user in ViewData, this approach might be helpful:

In the cshtml file

@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>

In addition to existing answers I'd like to add that you can also have a class instance available app-wide which holds user-related data like UserID etc.

It may be useful for refactoring e.g. you don't want to fetch UserID in every controller action and declare an extra UserID parameter in every method related to Service Layer.

I've done a research and here's my post.

You just extend your class which you derive from DbContext by adding UserId property (or implement a custom Session class which has this property).

At filter level you can fetch your class instance and set UserId value.

After that wherever you inject your instance - it will have the necessary data (lifetime must be per request, so you register it using AddScoped method).

Working example:

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

For more information see my answer.


Taking IdentityUser would also work. This is a current user object and all values of user can be retrieved.

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

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

If you are using the scafolded Identity and using Asp.net Core 2.2+ you can access the current user from a view like this:

@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


This is old question but my case shows that my case wasn't discussed here.

I like the most the answer of Simon_Weaver (https://stackoverflow.com/a/54411397/2903893). He explains in details how to get user name using IPrincipal and IIdentity. This answer is absolutely correct and I recommend to use this approach. However, during debugging I encountered with the problem when ASP.NET can NOT populate service principle properly. (or in other words, IPrincipal.Identity.Name is null)

It's obvious that to get user name MVC framework should take it from somewhere. In the .NET world, ASP.NET or ASP.NET Core is using Open ID Connect middleware. In the simple scenario web apps authenticate a user in a web browser. In this scenario, the web application directs the user’s browser to sign them in to Azure AD. Azure AD returns a sign-in response through the user’s browser, which contains claims about the user in a security token. To make it work in the code for your application, you'll need to provide the authority to which you web app delegates sign-in. When you deploy your web app to Azure Service the common scenario to meet this requirements is to configure web app: "App Services" -> YourApp -> "Authentication / Authorization" blade -> "App Service Authenticatio" = "On" and so on (https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md). I beliebe (this is my educated guess) that under the hood of this process the wizard adjusts "parent" web config of this web app by adding the same settings that I show in following paragraphs. Basically, the issue why this approach does NOT work in ASP.NET Core is because "parent" machine config is ignored by webconfig. (this is not 100% sure, I just give the best explanation that I have). So, to meke it work you need to setup this manually in your app.

Here is an article that explains how to manyally setup your app to use Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Step 1: Register the sample with your Azure AD tenant. (it's obvious, don't want to spend my time of explanations).

Step 2: In the appsettings.json file: replace the ClientID value with the Application ID from the application you registered in Application Registration portal on Step 1. replace the TenantId value with common

Step 3: Open the Startup.cs file and in the ConfigureServices method, after the line containing .AddAzureAD insert the following code, which enables your application to sign in users with the Azure AD v2.0 endpoint, that is both Work and School and Microsoft Personal accounts.

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

Summary: I've showed one more possible issue that could leed to an error that topic starter is explained. The reason of this issue is missing configurations for Azure AD (Open ID middleware). In order to solve this issue I propose manually setup "Authentication / Authorization". The short overview of how to setup this is added.


Perhaps I didn't see the answer, but this is how I do it.

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

You need to have change these values

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

Startup.cs --> ConfigureServices(...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC or Web Api Controller

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

Controller method:

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

Result is userName e.g. = Domain\username


I got my 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;
    }
}

참고URL : https://stackoverflow.com/questions/36641338/how-get-current-user-in-asp-net-core

반응형