如何在 asp.net 核心中获取当前用户

本文关键字:获取 用户 核心 asp net | 更新日期: 2024-09-12 13:49:31

我想获取当前用户,以便可以访问他们的电子邮件地址等字段。但我不能在核心 asp.net 做到这一点。这是我的代码:

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

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

如何在 asp.net 核心中获取当前用户

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

简单的工作方法,我检查了。

private readonly UserManager<IdentityUser> _userManager;
public CompetitionsController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}
var user = await _userManager.GetUserAsync(HttpContext.User);

然后你可以像user.Email这样的变量的所有属性。我希望这会帮助某人。

编辑

这是一件看似简单的事情,但 ASP.NET 核心中不同类型的身份验证系统有点复杂。我更新,因为有些人正在null.

对于 JWT 身份验证(在 ASP.NET Core v3.0.0-preview 7 上测试(:

var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
var user = await _userManager.FindByEmailAsync(email);

不得不说我很惊讶HttpContext在构造函数中是空的。我敢肯定这是出于性能原因。已确认使用如下所述的IPrincipal确实会将其注入构造函数中。它本质上与公认的答案相同,但以更界面的方式。

<小时 />

对于任何找到这个问题的人,正在寻找通用"如何获得当前用户?">的答案,您可以直接从Controller.User访问User。但是您只能在操作方法中执行此操作(我假设是因为控制器不仅使用 HttpContext 运行,而且出于性能原因(。

但是 - 如果您在构造函数中需要它(如 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获得它,你都会得到一个保证是实现IPrincipalClaimsPrincipal对象的对象。

目前没有其他类型的用户可供 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界面,我不确定。我假设 MS 认为 ClaimsPrincipal 只是一个专门的"集合",不需要接口。

有另一种方法让当前用户进入 Asp.NET 核心 - 我想我在这里的某个地方看到了它,在 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 将无法工作(不会编译( ;)

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

public string LoggedInUser => User.Identity.Name;

至少在Controller

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

  1. .Net Core --> Properties --> 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;

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

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

在启动中.cs

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

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

var httpContext = new HttpContextAccessor().HttpContext;

希望对;)有所帮助

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

它对于重构可能很有用,例如,您不想在每个控制器操作中获取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();
    }
}

有关更多信息,请参阅我的答案。

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

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

很明显,要获取用户名MVC框架,应该从某个地方获取它。在.NET世界中,ASP.NET 或 ASP.NET Core正在使用Open ID Connect中间件。在简单方案中,Web 应用在 Web 浏览器中对用户进行身份验证。在此方案中,Web 应用程序指示用户的浏览器将其登录到 Azure AD。 Azure AD 通过用户的浏览器返回登录响应,其中包含有关安全令牌中的用户的声明。若要使其在应用程序的代码中工作,需要提供 Web 应用委托登录的权限。将 Web 应用部署到 Azure 服务时,满足此要求的常见方案是配置 Web 应用:"应用服务"->"你的应用"->"身份验证/授权"边栏选项卡>"应用服务身份验证"="开",依此类推 (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。将租户 ID 值替换为通用

第 3 步:打开 Startup.cs 文件并在 配置服务 方法中,在包含 .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 中间件(的配置。为了解决这个问题,我建议手动设置"身份验证/授权"。添加了有关如何设置的简短概述。

采取IdentityUser也可以。这是一个当前用户对象,可以检索用户的所有值。

private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}
var user = await _userManager.GetUserAsync(HttpContext.User);

如果您使用的是折叠标识并使用 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

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

我确实想提一下,您需要在调试时检查项目设置,默认值为 Enable Anonymous Authentication = true

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 核心 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;

嗨,如果你愿意,你可以像这里一样在声明上获得id

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

对于那些想要获取 ASP.NET C# Web 应用程序的当前用户 - 即使在 IIS 上运行时,我个人在尝试了数小时后验证了我的研究中提到的许多其他选项。

  string userName = User.Identity.Name;

请确保 IIS 上的 Web 应用程序的身份验证包括 Windows 身份验证。另外,请确保您的程序.cs文件包含...

using Microsoft.AspNetCore.Authentication.Negotiate;
    builder.Services.AddControllersWithViews();
    
    builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
       .AddNegotiate();
    
    
    builder.Services.AddAuthorization(options =>
    {
        // By default, all incoming requests will be authorized according to the default policy.
        options.FallbackPolicy = options.DefaultPolicy;
    });

我得到了我的解决方案

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