Как получить текущего пользователя в ядре asp.net
Я хочу получить текущего пользователя для получения информации о пользователе, такой как электронная почта.
Но я не могу сделать это в ядре asp.net. я весьма озадачен
Это мой код
HttpContext
почти равен нулю в конструкторе контроллера.
Это не хорошо, чтобы получить пользователя в каждом действии. Я хочу получить информацию о пользователе один раз и установить ее в ViewData
;
public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
Ответы
Ответ 1
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.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
Ответ 2
Простой способ, который работает, и я проверил.
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);
Ответ 3
Есть еще один способ получить текущего пользователя в 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. Не будет работать без обоих ждать (не скомпилировать);)
Ответ 4
Похоже, что на данный момент (апрель 2017 года) работает следующее:
public string LoggedInUser => User.Identity;
По крайней мере, в пределах Controller
Ответ 5
Я должен сказать, что был довольно удивлен, что HttpContext является нулевым внутри конструктора. Я уверен, что по причинам производительности. Подтвердили, что при использовании 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
была просто специализированной "коллекцией", которая не гарантировала интерфейс.
Ответ 6
Моя проблема состояла в том, чтобы получить доступ к зарегистрированному пользователю как объект в файле 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>
Ответ 7
В дополнение к существующим ответам я хотел бы добавить, что у вас также может быть доступный экземпляр класса для всего приложения, который содержит данные, относящиеся к пользователю, такие как UserID
и т.д.
Это может быть полезно для рефакторинга, например, вы не хотите извлекать UserID
в каждом действии контроллера и объявлять дополнительный параметр UserID
в каждом методе, связанном с Service Layer.
Я провел исследование и вот мой пост.
Вы просто расширяете свой класс, производный от DbContext
, добавляя свойство UserId
(или реализуете собственный класс Session
который имеет это свойство).
На уровне фильтра вы можете получить экземпляр вашего класса и установить значение UserId
.
После этого, куда бы вы ни внедрили свой экземпляр - он будет иметь необходимые данные (время жизни должно быть на запрос, поэтому вы регистрируете его с AddScoped
метода 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();
}
}
Для получения дополнительной информации см. Мой ответ.
Ответ 8
Принимая IdentityUser
также будет работать. Это текущий объект пользователя, и все значения пользователя могут быть получены.
private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
var user = await _userManager.GetUserAsync(HttpContext.User);
Ответ 9
Если вы используете удостоверение в скаффолде и используете 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
Ответ 10
Это старый вопрос, но мой случай показывает, что мой случай здесь не обсуждался.
Мне больше всего нравится ответ Simon_Weaver (fooobar.com/questions/148447/...). Он подробно объясняет, как получить имя пользователя, используя IPrincipal и IIdentity. Этот ответ абсолютно правильный, и я рекомендую использовать этот подход. Однако во время отладки я столкнулся с проблемой, когда ASP.NET НЕ может правильно заполнить принцип обслуживания. (или, другими словами, IPrincipal.Identity.Name имеет значение null)
Очевидно, что для получения имени пользователя MVC framework его нужно откуда-то взять. В мире .NET ASP.NET или ASP.NET Core используют промежуточное программное обеспечение Open ID Connect.
В простом сценарии веб-приложения аутентифицируют пользователя в веб-браузере. В этом случае веб-приложение направляет браузер пользователей для входа в Azure AD. Azure AD возвращает ответ о входе через браузер пользователей, который содержит утверждения о пользователе в маркере безопасности.
Чтобы это работало в коде вашего приложения, вам необходимо предоставить полномочия, которым ваше веб-приложение делегирует вход в систему.
При развертывании веб-приложения в службе Azure общий сценарий, отвечающий этим требованиям, заключается в настройке веб-приложения: "Службы приложения" → YourApp → "Аутентификация/Авторизация" → "Аутентификация службы приложения" = "Вкл" и т.д.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 и School и Личные учетные записи Microsoft.
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
Резюме: я показал еще одну возможную проблему, которая могла привести к ошибке, описанной автором темы. Причиной этой проблемы является отсутствие конфигураций для Azure AD (промежуточное программное обеспечение Open ID). Для решения этой проблемы я предлагаю вручную настроить "Аутентификация/Авторизация". Краткий обзор того, как настроить это, добавлен.
Ответ 11
Возможно, я не видел ответа, но так я это делаю.
- .Net Core → Свойства → launchSettings.json
Вам нужно изменить эти значения
"windowsAuthentication": true, // needs to be true
"anonymousAuthentication": false, // needs to be false
Startup.cs → ConfigureServices (...)
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
MVC или контроллер Web Api
private readonly IHttpContextAccessor _httpContextAccessor;
//constructor then
_httpContextAccessor = httpContextAccessor;
Метод контроллера:
string userName = _httpContextAccessor.HttpContext.User.Identity.Name;
Результат - имя пользователя, например = Домен\имя пользователя
Ответ 12
Я получил свое решение
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;
}
}