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

Posted

技术标签:

【中文标题】如何在asp.net core中获取当前用户【英文标题】:How to get current user in asp.net core 【发布时间】:2016-08-07 02:00:32 【问题描述】:

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

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

public DashboardController()

    var user = HttpContext.User.GetUserId();

【问题讨论】:

与 MVC 或 Web APi 一起使用? 【参考方案1】:
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 给出用户名。 谁能告诉我 UserPrincipal.Current.Name 出了什么问题? @ademcaglin 出于某种原因,在我的情况下,用户返回null?我使用.Net core 2.1 Web api【参考方案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);

【讨论】:

在 asp.net Core 2.0 的控制器中非常适合我 什么是_userManager? 在 ASP.NET Core Identity 中,用户管理器是依赖注入提供的用于创建用户的服务。请参阅docs 了解更多信息: 如何在非异步方法中实现? 对我来说返回 null。为什么?【参考方案3】:

我不得不说我很惊讶 HttpContext 在构造函数中为空。我确定这是出于性能原因。已确认使用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 代表用户和用户名。 Wikipedia will comfort you if 'Principal' sounds odd.

重要的是要认识到,无论您是从IHttpContextAccessor.HttpContext.UserControllerBase.User 还是ControllerBase.HttpContext.User 获得它,您都获得了一个保证是实现ClaimsPrincipalClaimsPrincipal 对象@ 对象.

目前没有 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,而只需要模拟代表IPrincipalwhich can just beClaimsPrincipal的东西。

还有一件我不能 100% 确定的额外重要的事情。如果您需要访问来自ClaimsPrincipal 的实际声明,则需要将IPrincipal 转换为ClaimsPrincipal。这很好,因为我们 100% 知道在运行时它是那种类型(因为这就是 HttpContext.User 是)。我实际上喜欢在构造函数中执行此操作,因为我已经确定任何IPrincipal 都将是ClaimsPrincipal

如果您正在模拟,只需 create a ClaimsPrincipal directly 并将其传递给任何需要 IPrincipal

究竟为什么IClaimsPrincipal 没有接口我不确定。我假设 MS 认为 ClaimsPrincipal 只是一个专门的“集合”,不需要接口。

【讨论】:

这允许您在应用程序的任何位置注入当前用户,很好的答案! 这不起作用。对于注入的IPrincipal,我总是得到null。我还需要将临时服务添加为…GetService&lt;IHttpContextAccessor&gt;()?.HttpContext.User…(使用?),否则它会崩溃(GetService 返回 null)。 你可以做 services.AddTransient(provider =&gt; provider.GetService&lt;IHttpContextAccessor&gt;().HttpContext.User); 因为 HttpContext.User 是一个 ClaimsPrincipal。【参考方案4】:

有另一种在 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 的控制器。没有两个 await 将无法工作(不会编译);)

【讨论】:

这需要使用Identity 什么是ApplicationUser? ApplicationUser 通常继承自 IdentityUser,因此可以使用其他属性等进行扩展。【参考方案5】:

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

public string LoggedInUser => User.Identity.Name;

至少在Controller

【讨论】:

您不能将类型“System.Security.Principal.IIdentity”隐式转换为“字符串”。 字符串 LoggedInUser = User.Identity.Name; 作为以前没有见过=&gt; 运算符这样使用的人,它被称为“表达式体定义”,在this documentation 中有描述。以防将来像我这样的人想知道。 您的代码不可能在编辑之前编译,因为没有从IIdentitystring 的转换,正如顶部评论中所述。编辑只是解决了这个问题。我也不确定您是如何得出结论的(特别是因为“编辑”积分仅提供给声誉低于 2k 的用户)。【参考方案6】:

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

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

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

【讨论】:

这也与托管有关。您需要在 IIs 中禁用匿名身份验证才能使其正常工作。【参考方案7】:

我的问题是将登录的用户作为 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 类上的公司导航属性的公司名称)。没有找到包含导航属性的方法。【参考方案8】:

除了现有的答案,我想补充一点,您还可以在应用程序范围内使用一个类实例,其中包含与用户相关的数据,例如 UserID 等。

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

我做了一项研究,这里是my post。

您只需通过添加UserId 属性扩展您从DbContext 派生的类(或实现具有此属性的自定义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();
    

欲了解更多信息,请参阅my answer。

【讨论】:

【参考方案9】:

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

在 StartUp.cs 中

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

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

var httpContext = new HttpContextAccessor().HttpContext;

希望对你有所帮助;)

【讨论】:

【参考方案10】:

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

我最喜欢 Simon_Weaver (https://***.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 应用程序:“应用程序服务”-> YourApp ->“身份验证/授权”刀片->“应用程序服务身份验证”=“开启”等等上(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。 将 TenantId 值替换为 common

第 3 步:打开 Startup.cs 文件并在 ConfigureServices 方法中,在包含 .AddAzureAD 的行之后插入以下代码,这使您的应用程序能够使用 Azure AD v2.0 端点登录用户,即两者都是 Work以及学校和 Microsoft 个人帐户。

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>

    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
);

总结:我已经展示了另一个可能的问题,它可能导致主题启动器被解释的错误。此问题的原因是缺少 Azure AD(Open ID 中间件)的配置。为了解决这个问题,我建议手动设置“身份验证/授权”。添加了如何设置的简短概述。

【讨论】:

【参考方案11】:

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

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

    _userManager = userManager;


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

【讨论】:

【参考方案12】:

如果您使用支架式身份并使用 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

【讨论】:

【参考方案13】:

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

我确实想提一下,在调试时你会想要检查你的项目设置,默认是Enable Anonymous Authentication = true

【讨论】:

【参考方案14】:
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;

【讨论】:

【参考方案15】:

在探索了许多解决方案之后,这就是我使用 ASP.NET core 5 的方法。

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

如上面的 sn-p 所示,添加自定义“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 );
        

【讨论】:

【参考方案16】:

我使用@Ahmed 为身份提供的答案

为了获取当前的用户 id,我使用下面的

 var currentuserid = userManager.GetUserId(User);

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

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

【讨论】:

【参考方案17】:

您好,如果您愿意,可以在此处获取索赔 ID

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

【讨论】:

【参考方案18】:

我找到了解决办法

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;
    

【讨论】:

虽然此代码可以回答问题,但提供有关 如何为什么 解决问题的附加上下文将提高​​答案的长期价值。

以上是关于如何在asp.net core中获取当前用户的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ASP.NET Core RC2 MVC6 和 IIS7 获取当前 Windows 用户

ASP.Net Core 如何在 EF Core 和 Identity 中获取用户角色

如何在 ASP.NET Core 5 MVC (.NET 5) 中获取记录的用户数据?

如何在 Asp.net core mvc 的控制器中获取自己的用户

如何在 ASP.NET Core 中获取 HttpContext.Current? [复制]

登录 ASP.NET Core 3.1 后如何获取用户信息