Як отримати поточного користувача в ядрі asp.net


128

Я хочу отримати поточного користувача для отримання інформації про користувача, наприклад електронну пошту. Але я не можу цього зробити в ядрі asp.net. Я так розгублений Це мій код.

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

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

5
Використовуєте з MVC або Web APi?
Тушар

Відповіді:


172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT для конструктора

Нижче код працює:

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

Редагувати для RTM

Ви повинні зареєструватися IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }

2
він працює в action.but я хочу використовувати в конструкторі контролера.
Мехран Хафізі

3
чи можна використовувати це на заняттях?
Мехран Хафізі

5
ClaimTypes.NameIdentifierдає поточний ідентифікатор користувача та ClaimTypes.Nameдає ім'я користувача.
Микола Костов

3
Хтось може сказати мені, що не так з UserPrincipal.Current.Name?
tipura

2
@ademcaglin З деяких причин Користувач повертається nullв моєму випадку? Я використовую, .Net core 2.1 Web apiхоча.
Шруті Варгезе

54

Простий спосіб, який працює, і я перевірив.

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

1
Для мене чудово працює всередині контролера в asp.net Core 2.0
jmdon

2
що таке _userManager?
NullVoxPopuli

6
В основній ідентичності ASP.NET, менеджер користувачів - це послуга, що надається залежним введенням для створення користувачів. Докладнішу інформацію див. У документах :
Ахмад

2
Як цього можна досягти методом без асинхронізації?
T3.0

Для мене повернення недійсне. Чому?
Альберто Клаудіо Мандлат

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. Не буде працювати без обох очікувань (не збирається);)


Це вимагає використання ідентичності
Fraze

1
Що таке ApplicationUser?
Майк

ApplicationUser зазвичай успадковується від IdentityUser, тому його можна розширити додатковими властивостями тощо
Corgalore

20

Треба сказати, що я був дуже здивований, що HttpContext є нульовим усередині конструктора. Я впевнений, що це з міркувань продуктивності. Підтвердили, що, IPrincipalяк описано нижче, вводять його в конструктор. По суті, це робиться так само, як прийнята відповідь, але більш інтерфейсно.


Кожен, хто знайде це питання, шукає відповідь на загальне "Як отримати поточного користувача?" Ви можете просто отримати доступ Userбезпосередньо з Controller.User. Але ви можете це робити лише у внутрішніх методах дій (я припускаю, що контролери не працюють лише з HttpContexts і з міркувань продуктивності).

Однак - якщо вам це потрібно в конструкторі (як це робилося в ОП) або потрібно створити інші об'єкти, які можна вводити, які потребують поточного користувача, то нижче - кращий підхід:

Введіть 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це просто спеціалізована «колекція», яка не гарантує інтерфейс.


2
Це дозволяє вводити поточного користувача будь-де у вашій програмі, чудова відповідь!
Мачадо

1
Це не працює. Я завжди отримую nullза ін’єкції IPrincipal. Мені також потрібно було додати перехідну службу як …GetService<IHttpContextAccessor>()?.HttpContext.User…(разом із ?), оскільки вона в іншому випадку вийде з ладу (GetService повернув нуль).
ygoe

Ви можете просто зробити так, services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);як HttpContext.User - це претензії.
Джей Зелос

18

Здається, що на даний момент (квітень 2017 року) працює наступне:

public string LoggedInUser => User.Identity.Name;

Принаймні, перебуваючи в межах Controller


4
Ви не можете неявно перетворити тип 'System.Security.Principal.IIdentity' у 'string'.
Ентоні Хуанг

3
рядок LoggedInUser = User.Identity.Name;
Alic W

5
Оскільки той, хто раніше не бачив =>оператора, який використовувався таким чином, його називають "Визначення тіла виразів" і описано в цій документації . На всякий випадок, коли такі люди, як я, задаються питанням.
Натан Клімент

Ваш код не міг скласти до редагування, враховуючи те, що відсутня конверсія IIdentityв string, як це також зазначено в головному коментарі. Правка це просто виправила. Я не впевнений, як ви дійшли свого висновку (зокрема, оскільки "редакторські" бали надаються лише користувачам, які не мають репутації 2k).
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;

Результатом є userName, наприклад = Домен \ ім'я користувача


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>

Будь-яка ідея, як ви можете отримати властивість навігації для завантаження (назва компанії навігаційної властивості компанії в моєму класі 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);

0

Якщо ви використовуєте ліцензований ідентифікатор і використовуєте 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 є нульовим)

Очевидно, що щоб отримати ім'я користувача MVC Framework, слід взяти його звідкись. У світі .NET ASP.NET або ASP.NET Core використовує програмне забезпечення Open ID Connect. У простому сценарії веб-додатки аутентифікують користувача у веб-браузері. У цьому випадку веб-додаток спрямовує браузер користувача на вхід у Azure AD. Azure AD повертає відповідь про вхід через браузер користувача, який містить претензії про користувача в маркері безпеки. Щоб він працював у коді для вашої програми, вам потрібно надати повноваження, до якого ви входите делегати веб-додатків. Коли ви розгортаєте веб-додаток до служби Azure, загальним сценарієм для задоволення цих вимог є налаштування веб-програми: "Служби додатків" -> YourApp -> лезо "Автентифікація / авторизація" -> "Служба додатків Authenticatio" = "Увімкнено"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, тобто і Work and School, і Особисті акаунти Microsoft.

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

Короткий зміст : Я показав ще одну можливу проблему, яка могла б призвести до помилки, що пояснюється початком теми. Причиною цієї проблеми є відсутність конфігурацій для Azure AD (Open ID midware). Для вирішення цього питання я пропоную налаштування вручну "Автентифікація / Авторизація". Додано короткий огляд того, як налаштувати це.


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
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, як і чому він вирішує проблему, покращить довгострокове значення відповіді.
Олександр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.