我想要一个当前用户来获取用户的信息,例如电子邮件。但是我不能在asp.net核心中做到这一点。我很困惑这是我的代码。
HttpContext
在控制器的构造函数中几乎为null 。在每个动作中都吸引用户是不好的。我想一次获取用户信息并设置为ViewData
;
public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
我想要一个当前用户来获取用户的信息,例如电子邮件。但是我不能在asp.net核心中做到这一点。我很困惑这是我的代码。
HttpContext
在控制器的构造函数中几乎为null 。在每个动作中都吸引用户是不好的。我想一次获取用户信息并设置为ViewData
;
public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
Answers:
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();
}
ClaimTypes.NameIdentifier
提供当前的用户ID,并ClaimTypes.Name
提供用户名。
null
,我的情况下用户正在返回?我正在使用.Net core 2.1 Web api
。
简单的方法有效,我检查了一下。
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中获取当前用户的方法-我想我在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的控制器。没有两个等待就不会工作(不会编译);)
我不得不说我很惊讶HttpContext在构造函数中为null。我确定是出于性能原因。已确认IPrincipal
按如下所述使用确实会将其注入到构造函数中。它的作用基本上与接受的答案相同,但是采用的是界面化的方式。
对于任何发现此问题的人,都希望找到通用的“如何获得当前用户?”的答案。您可以User
直接从访问Controller.User
。但是您只能在操作方法内部执行此操作(我想是因为控制器不仅出于性能原因而仅与HttpContexts一起运行)。
但是,如果您在构造函数中需要它(如OP一样),或者需要创建需要当前用户的其他可注入对象,则下面是一种更好的方法:
第一次见到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
代表用户和用户名。如果'Principal'听起来很奇怪,Wikipedia将为您提供安慰。
重要的是要意识到,不管你得到它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
并将其传递给takes即可IPrincipal
。
到底为什么没有接口,IClaimsPrincipal
我不确定。我认为MS认为那ClaimsPrincipal
只是一个不需要接口的专门“集合”。
null
为注射而努力IPrincipal
。我还需要将临时服务添加为…GetService<IHttpContextAccessor>()?.HttpContext.User…
(带有?
),因为否则它将崩溃(GetService返回null)。
services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
作为HttpContext.User做为ClaimsPrincipal。
截至目前(2017年4月),似乎可以使用以下功能:
public string LoggedInUser => User.Identity.Name;
至少在 Controller
IIdentity
到的转换,因此您的代码可能无法在编辑之前进行编译string
(如顶部注释中所述)。编辑只是解决了这个问题。我也不确定您是如何得出结论的(特别是因为“编辑”分数仅授予信誉低于2k的用户)。
也许我没有找到答案,但这就是我的方法。
您需要更改这些值
"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;
结果是用户名,例如=域\用户名
我的问题是将登录的用户作为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>
除了现有的答案,我想补充一下,您还可以在整个应用范围内使用一个类实例,其中包含与用户相关的数据,例如UserID
etc。
这对于重构 例如 您不希望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();
}
}
有关更多信息,请参阅我的答案。
如果使用脚手架身份和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>
}
这是个老问题,但我的案子表明这里没有讨论我的案子。
我最喜欢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中间件。在简单的场景中,Web应用程序在Web浏览器中对用户进行身份验证。在这种情况下,Web应用程序指导用户的浏览器将他们登录到Azure AD。Azure AD通过用户的浏览器返回登录响应,该响应包含安全令牌中有关用户的声明。为了使其在您的应用程序代码中起作用,您需要提供Web应用程序委派登录所使用的权限。将Web应用程序部署到Azure Service时,满足此要求的常见方案是配置Web应用程序:“ App Services”-> YourApp->“身份验证/授权”刀片->“ App Service Authenticatio” =“打开”https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md)。我相信(这是我的有根据的猜测),在此过程的指导下,向导通过添加与我在以下段落中显示的相同设置来调整此Web应用程序的“父” Web配置。基本上,为什么此方法在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的“应用程序注册”门户中注册的应用程序中的应用程序ID。
步骤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(开放ID中间件)的配置。为了解决此问题,我建议手动设置“身份验证/授权”。添加了有关如何设置的简短概述。
大多数答案都显示了如何最好地HttpContext
利用文档,这也是我所使用的。
我确实想提一提,您将在调试时检查项目设置,默认值为Enable Anonymous Authentication = true
。
我有解决方案
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;
}
}