Как получить текущего пользователя в ядре asp.net

129

Я хочу получить текущего пользователя для получения информации о пользователе, например электронной почты. Но я не могу этого сделать в ядре asp.net. Я так запуталась. Это мой код.

HttpContextпочти равно нулю в конструкторе контроллера. Нехорошо привлекать пользователя к каждому действию. Я хочу получить информацию о пользователе один раз и установить его ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Мехран Хафизи
источник
5
Используете MVC или Web APi?
Тушар

Ответы:

172
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.AddHttpContextAccessor();
    }
Адем Каглин
источник
2
он работает в действиях. но я хочу использовать в конструкторе контроллера.
Мехран Хафизи
3
можно ли использовать это в классах?
Мехран Хафизи
5
ClaimTypes.NameIdentifierдает текущий идентификатор пользователя и ClaimTypes.Nameдает имя пользователя.
Николай Костов
3
Кто-нибудь может сказать мне, что не так с UserPrincipal.Current.Name?
типура
2
@ademcaglin По каким причинам Пользователь возвращается nullв моем случае? .Net core 2.1 Web apiХотя я использую .
Срути Варгезе
55

Простой способ, который работает, и я проверил.

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);
Ahmad
источник
1
отлично работает для меня внутри контроллера в asp.net Core 2.0
jmdon
2
что такое _userManager?
NullVoxPopuli
6
В ASP.NET Core Identity диспетчер пользователей - это служба, предоставляемая Dependency Inject для создания пользователей. См. Документы для получения дополнительной информации:
Ахмад
2
Как этого можно достичь неасинхронным методом?
T3.0 02
Для меня возвращается null. Зачем?
Альберто Клаудио Мандлате
22

Есть другой способ получить текущего пользователя в Asp.NET Core - и я думаю, что видел его где-то здесь, на 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;
}  

Этот код переходит в контроллер с именем DemoController. Не будет работать без обоих await (не компилируется);)

Hekkaryk
источник
Это требует использования идентичности
Фрейз
1
Что такое ApplicationUser?
Майк,
ApplicationUser обычно наследуется от IdentityUser, поэтому его можно расширить с помощью дополнительных свойств и т. Д.
Коргалор,
20

Я должен сказать, что был очень удивлен, что HttpContext имеет значение null внутри конструктора. Я уверен, что это из соображений производительности. Подтвердили, что при использовании, IPrincipalкак описано ниже, он вводится в конструктор. По сути, он делает то же самое, что и принятый ответ, но более интерфейсным способом.


Для тех, кто нашел этот вопрос и ищет ответ на общий вопрос «Как получить текущего пользователя?» вы можете просто получить доступ Userпрямо из Controller.User. Но вы можете делать это только внутри методов действий (я предполагаю, потому что контроллеры работают не только с HttpContexts и по соображениям производительности).

Однако - если вам это нужно в конструкторе (как это сделал OP) или вам нужно создать другие инъекционные объекты, которым нужен текущий пользователь, то ниже будет лучший подход:

Внедрить IPrincipal, чтобы получить пользователя

Первая встреча IPrincipalиIIdentity

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

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

IPrincipalи IIdentityпредставляет пользователя и имя пользователя. Википедия утешит вас, если «Директор» звучит странно .

Важно понимать, что независимо от того, получаете ли вы это 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);

Затем в вашем объекте DI, которому нужен пользователь, которого вы просто вводите, IPrincipalчтобы получить текущего пользователя.

Самым важным здесь является то, что если вы выполняете модульные тесты, вам не нужно отправлять HttpContext, а нужно только имитировать то, что представляет собой то, IPrincipal что может быть ClaimsPrincipal .

Еще одна важная вещь, в которой я не уверен на 100%. Если вам нужно получить доступ к фактическим заявкам от ClaimsPrincipalвас, необходимо преобразовать их IPrincipalв ClaimsPrincipal. Это нормально, поскольку мы на 100% знаем, что во время выполнения он относится к этому типу (раз уж он HttpContext.Userесть). На самом деле мне нравится просто делать это в конструкторе, поскольку я уже точно знаю, что любой из них IPrincipal будет ClaimsPrincipal.

Если вы делаете насмешку, просто создайте файл ClaimsPrincipalнапрямую и передайте его всем, что нужно IPrincipal.

IClaimsPrincipalЯ не уверен, почему именно для этого нет интерфейса . Я предполагаю, что MS решила, что ClaimsPrincipalэто просто специализированная «коллекция», для которой не нужен интерфейс.

Simon_Weaver
источник
2
Это позволяет вам внедрить текущего пользователя в любое место вашего приложения, отличный ответ!
Machado
1
Это не работает. Я всегда получаю nullза укол IPrincipal. Мне также нужно было добавить временную службу как …GetService<IHttpContextAccessor>()?.HttpContext.User…?), потому что в противном случае она вылетела бы из строя (GetService вернул значение null).
ygoe
Вы можете просто сделать так, services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);как HttpContext.User - это ClaimsPrincipal.
Джей Зелос,
18

Похоже, что на данный момент (апрель 2017 года) работает следующее:

public string LoggedInUser => User.Identity.Name;

По крайней мере, в пределах Controller

Grandizer
источник
4
Вы не можете неявно преобразовать тип System.Security.Principal.IIdentity в строку.
Энтони Хуанг,
3
строка LoggedInUser = User.Identity.Name;
Alic W
5
Как человек, который раньше не видел, чтобы этот =>оператор использовался таким образом, он называется «Определение тела выражения» и описан в этой документации . На всякий случай будущие люди вроде меня задаются вопросом.
Натан Клемент
Ваш код невозможно было скомпилировать до редактирования, учитывая, что нет преобразования из IIdentityв string, как также указано в верхнем комментарии. Редакция просто исправила это. Я также не уверен, как вы пришли к своему выводу (в частности, поскольку «редакторские» баллы начисляются только пользователям с репутацией ниже 2к).
fuglede
9

Возможно, я не видел ответа, но я так делаю.

  1. .Net Core -> Свойства -> launchSettings.json

Вам необходимо изменить эти значения

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

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC или веб-контроллер API

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

Метод контроллера:

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

Результат - имя пользователя, например = Домен \ имя пользователя

Том Стикель
источник
4

Моя проблема заключалась в том, чтобы получить доступ к зарегистрированному пользователю как объекту в файле cshtml. Учитывая, что вам нужен пользователь во ViewData, этот подход может быть полезным:

В файле 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
источник
Любая идея, как вы можете получить свойство навигации для загрузки (название компании свойства навигации компании в моем классе ApplicationUser). Не видел способа включить свойства навигации.
Хантер Нельсон
1

В дополнение к существующим ответам я хотел бы добавить, что у вас также может быть экземпляр класса, доступный для всего приложения, который содержит данные, связанные с пользователем, например UserIDи т. Д.

Это может быть полезно для рефакторинга, например вы не хотите получать UserIDв каждом действии контроллера и объявлять дополнительный UserIDпараметр в каждом методе, относящемся к уровню обслуживания.

Я провел исследование и вот мой пост .

Вы просто расширяете свой класс, от которого вы производите DbContext, добавляя UserIdсвойство (или реализуя собственный Sessionкласс, у которого есть это свойство).

На уровне фильтра вы можете получить экземпляр класса и установить UserIdзначение.

После этого куда бы вы ни внедрили свой экземпляр - у него будут необходимые данные (время жизни должно быть на запрос , поэтому вы регистрируете его с помощью AddScopedметода).

Рабочий пример:

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

Для получения дополнительной информации см. Мой ответ .

Алекс Герман
источник
0

Взять IdentityUserтоже подойдет. Это текущий пользовательский объект, и все значения пользователя могут быть получены.

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

var user = await _userManager.GetUserAsync(HttpContext.User);
zzdhxu
источник
0

Если вы используете scafolded Identity и Asp.net Core 2.2+, вы можете получить доступ к текущему пользователю из следующего вида:

@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

Это старый вопрос, но мой случай показывает, что мой случай здесь не обсуждался.

Больше всего мне нравится ответ Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Он подробно объясняет, как получить имя пользователя с помощью IPrincipal и IIdentity. Это абсолютно правильный ответ, и я рекомендую использовать этот подход. Однако во время отладки я столкнулся с проблемой, когда ASP.NET НЕ может правильно заполнить принцип службы . (или, другими словами, IPrincipal.Identity.Name имеет значение null)

Очевидно, что для получения имени пользователя MVC-фреймворк должен откуда-то его взять. В мире .NET ASP.NET или ASP.NET Core используют промежуточное ПО Open ID Connect. В простом сценарии веб-приложения аутентифицируют пользователя в веб-браузере. В этом сценарии веб-приложение указывает браузеру пользователя выполнить вход в Azure AD. Azure AD возвращает ответ на вход через браузер пользователя, который содержит утверждения о пользователе в токене безопасности. Чтобы он работал в коде вашего приложения, вам необходимо предоставить полномочия, которым ваше веб-приложение делегирует вход. При развертывании веб-приложения в службе Azure обычно выполняется настройка веб-приложения: «Службы приложений» -> Ваше приложение -> колонка «Аутентификация / авторизация» -> «Проверка подлинности службы приложений» = «Вкл.»https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Я полагаю (это мое обоснованное предположение), что под капотом этого процесса мастер настраивает «родительскую» веб-конфигурацию этого веб-приложения, добавляя те же настройки, которые я показываю в следующих абзацах. По сути, проблема, почему этот подход НЕ работает в ASP.NET Core, заключается в том, что конфигурация «родительской» машины игнорируется webconfig. (я не уверен на 100%, я просто даю лучшее объяснение, которое у меня есть). Итак, чтобы заставить его работать, вам нужно настроить это вручную в своем приложении.

Вот статья, в которой объясняется, как многократно настроить приложение для использования Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Шаг 1. Зарегистрируйте образец в своем клиенте Azure AD. (это очевидно, не хочу тратить свое время на объяснения).

Шаг 2. В файле appsettings.json: замените значение ClientID идентификатором приложения из приложения, которое вы зарегистрировали на портале регистрации приложений на шаге 1. Замените значение TenantId общим

Шаг 3. Откройте файл Startup.cs и в методе ConfigureServices после строки, содержащей .AddAzureAD, вставьте следующий код, который позволяет вашему приложению входить в систему пользователей с конечной точкой Azure AD v2.0, то есть рабочей и учебной и Личные учетные записи Microsoft.

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

Резюме : я показал еще одну возможную проблему, которая может привести к ошибке, в которой объясняется тема для начала. Причина этой проблемы - отсутствие конфигурации для Azure AD (промежуточное ПО Open ID). Для решения этой проблемы предлагаю вручную настроить «Аутентификацию / Авторизацию». Добавлен краткий обзор того, как это настроить.

Роман Ойра-Ойра
источник
0

Большинство ответов показывают, как лучше всего справиться HttpContextс документацией, что я тоже сделал.

Я хотел упомянуть, что вы захотите проверить настройки проекта при отладке, по умолчанию это Enable Anonymous Authentication = true.

Хуан Эммануэль Афабле
источник
-1

Я получил свое решение

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
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и почему он решает проблему, улучшило бы долгосрочную ценность ответа.
Александр