如何在ASP.NET核心中获取当前用户

我想获得当前用户,这样我就可以访问他们的电子邮件地址等字段。 但我无法在ASP.NET核心中做到这一点。 这是我的代码:

HttpContext在控制器的构造函数中几乎为空。 在每个操作中获得一个用户是不好的。我想获取用户的信息一次,并将其保存到ViewData

public DashboardController()
{
var user = HttpContext.User.GetUserId();
}
319139 次浏览
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();
}

在ASP.NET核心中有另一种获取当前用户的方法-我想我在这里的某个地方看到了它,所以^^

// 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的控制器。如果不同时等待(不会编译),将无法工作;)

很简单的方法,我检查过了。

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


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

然后,您可以将此变量的所有属性(如_ABC)_0。我希望这能帮助一些人。

编辑

这是一件看似简单的事情,但由于ASP.NET核心中不同类型的身份验证系统而变得有点复杂。我更新,因为有些人得到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);

截至目前(2017年4月),似乎有以下工程:

public string LoggedInUser => User.Identity.Name;

至少在Controller

我的问题是将登录用户作为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>

我有办法了。

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

除了现有的答案之外,我想补充的是,你还可以在应用程序范围内使用一个类实例,它可以保存与用户相关的数据,比如_、ABC、_0等等。

它可能对重构例如,您不希望在每个控制器操作中获取UserID,并在与服务层相关的每个方法中声明额外的UserID参数。有用

我做了一个研究,这里有我的帖子

您只需通过添加UserId属性(或实现具有此属性的自定义Session类)来扩展从DbContext派生的类。

在过滤器级别,您可以获取您的类实例,并设置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();
}
}

有关详细信息,请参阅我的回答

IdentityUser也将起作用。这是当前用户对象,可以检索用户的所有值。

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


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

我不得不说,我对HttpContext在构造函数中为null感到非常惊讶。我肯定这是出于表演的原因。我已经确认,如下所述使用IPrincipal确实可以将其注入到构造函数中。它本质上与公认的答案相同,但以一种更具交互性的方式。


如果有人在寻找通用";如何获取当前用户?";的答案时发现了这个问题,您可以直接从Controller.User访问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表示用户和用户名。如果“校长”这个词听起来很奇怪,维基百科会安慰你。

重要的是要认识到,无论您是从IHttpContextAccessor.HttpContext.UserControllerBase.User还是ControllerBase.HttpContext.User获取数据,您都是在获取保证是_ABC_3对象的对象,该对象实现IPrincipal

目前,ASP.NET还没有将其他类型的用户用于User(但这并不是说其他用户不能实现IPrincipal)。

因此,如果你有一些你想注入的“当前用户名”的依赖项,你应该注入IPrincipal,而绝对不是IHttpContextAccessor

重要:不要浪费时间将IPrincipal直接注入到您的控制器或操作方法-这是毫无意义的,因为User已经提供给您。

startup.cs中:

   // Inject IPrincipal
services.AddHttpContextAccessor();
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没有接口。我假设微软决定ClaimsPrincipal只是一个专门的'集合',不保证接口。

如果您使用的是scafolded身份,并且使用的是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://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio.

这是一个老问题,但我的情况表明,我的情况没有在这里讨论。

我最喜欢西蒙_韦弗的回答(https://stackoverflow.com/a/54411397/2903893)。他详细解释了如何使用IPrincipal和IIdentity获取用户名。这个答案是绝对正确的,我建议使用这种方法。但是,在调试过程中,当ASP.NET无法正确填充服务原则时,我遇到了问题。(或者换句话说,IPrincipal.Identity.Name为空)

很明显,要获得用户名,MVC框架应该从某个地方获取它。在.NET世界中,ASP.NET或ASP.NET核心正在使用Open ID Connect中间件。 在简单的场景中,Web应用程序在Web浏览器中对用户进行身份验证。在这种情况下,Web应用程序指示用户的浏览器将他们登录到Azure AD.Azure AD通过用户的浏览器返回登录响应,该响应在安全令牌中包含有关用户的声明。 要使其在应用程序的代码中工作,您需要提供Web应用程序委派登录的权限。 将Web应用部署到Azure服务时,满足此要求的常见方案是配置Web应用:“ App Services ”->YourApp->“ Authentication/Authorization ” Blade->“ App Service Authenticatio ”=“ on ”等等(https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md)。我相信(这是我有根据的猜测),在这个过程中,向导通过添加我在下面的段落中显示的相同设置来调整这个Web应用程序的“父” Web配置。 基本上,这种方法在ASP.NET核心中不起作用的原因是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. 将TenantId值替换为Common

步骤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(开放ID中间件)的配置。为了解决这个问题,我建议手动设置“身份验证/授权”。添加了有关如何进行此设置的简短概述。

也许我没有看到答案,但我就是这样做的。

  1. .NET核心-->属性-->LaunchSettings.JSON

您需要更改这些值

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

Startup.CS->配置保留(.)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC或Web API控制器

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

控制器方法:

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

结果是用户名,例如=域\用户名

大多数答案都显示了如何最好地处理文档中的HttpContext,这也是我所使用的。

我想说的是,在调试时,您需要检查项目设置,默认设置是Enable Anonymous Authentication = true

我知道这里有很多正确答案,关于所有这些,我介绍这个黑客:

在Startup.CS中

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

然后你可以在任何需要HttpContext的地方使用:

var httpContext = new HttpContextAccessor().HttpContext;

希望能有所帮助;)

if (access token in header or query parameter)
{
// Set the claims like in the Account/Login action from the interactive login form
var claims = ...;
// Local helper method, is used in other places, too
var claimsIdentity = await SignInAsync(httpContext, claims, false);
// Set user for the current request
// This works in that it's in User.Identity, but the auth events won't fire
httpContext.User = new ClaimsPrincipal(claimsIdentity);
}

var userEmail = HttpContext.User.FindFirst(ClaimTypes.Email).Value;

在探索了许多解决方案之后,下面是对我有用的ASP.NET Core 5。

        var claims = new List<Claim>(){
new Claim("Id", _user.Id)
};

如上面的代码片段所示,添加自定义";ID";输入并将其设置为用户ID,同时准备要包含在JWT令牌生成中的声明列表。 然后只需使用该声明访问用户(此方法通过用户的ID唯一标识用户)。

        var userEmail = User.FindFirstValue("Id");
var user = await _userManager.FindByIdAsync(userEmail);

以下是完整的解决方案: ->;令牌生成帮助器方法

        public async Task<string> CreateToken()
{
var signingCredentials = GetSigningCredentials();
var claims = await GetClaims();
var tokenOptions = GenerateTokenOptions(signingCredentials, claims);
return new JwtSecurityTokenHandler().WriteToken(tokenOptions);
}
    

private SigningCredentials GetSigningCredentials()
{
var key = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWT_SECRET"));
var secret = new SymmetricSecurityKey(key);
return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
}
private async Task<List<Claim>> GetClaims()
{
var claims = new List<Claim>(){
new Claim("Id", _user.Id)
};
return claims;
}
private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, List<Claim> claims)
{
var jwtSettings = _configuration.GetSection("JwtSettings");
var tokenOptions = new JwtSecurityToken(
issuer: jwtSettings.GetSection("ValidIssuer").Value,
audience: jwtSettings.GetSection("ValidAudience").Value,
expires: DateTime.Now.AddMinutes(Convert.ToDouble(jwtSettings.GetSection("ExpiresIn").Value)),
signingCredentials: signingCredentials,
claims: claims
);
return tokenOptions;
}

下面是获取登录用户的代码:

        [HttpGet("user")]
public async Task<ActionResult<User>> GetUser()
{
var userId = User.FindFirstValue("Id");
var user = await _userManager.FindByIdAsync(userId);
return Ok(new { User = User });
}

我使用@Ahmed提供的答案进行身份验证

为了获得当前用户ID,我使用了以下代码

 var currentuserid = userManager.GetUserId(User);

为了在AspNetUsers表中获取与登录用户相关的其他字段,我使用了以下内容

var userorg = context.Users.Where(l=>l.Id== currentuserid).FirstOrDefaultAsync().Result.OrganizationId;

嗨,如果你愿意,你可以像这里一样在索赔上得到身份证。

   var userId = User.Claims.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Sub).Value;