如何在ASP.NET Core中获得当前用户


128

我想要一个当前用户来获取用户的信息,例如电子邮件。但是我不能在asp.net核心中做到这一点。我很困惑这是我的代码。

HttpContext在控制器的构造函数中几乎为null 。在每个动作中都吸引用户是不好的。我想一次获取用户信息并设置为ViewData;

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

5
与MVC或Web APi一起使用?
Tushar

Answers:


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

2
它在actions。工程中工作,但我想在控制器的构造函数中使用。
Mehran Hafizi

3
有可能在课堂上使用它吗?
Mehran Hafizi

5
ClaimTypes.NameIdentifier提供当前的用户ID,并ClaimTypes.Name提供用户名。
Nikolay Kostov

3
有人可以告诉我UserPrincipal.Current.Name怎么了吗?
tipura '18

2
@ademcaglin由于某些原因null,我的情况下用户正在返回?我正在使用.Net core 2.1 Web api
Sruthi Varghese

54

简单的方法有效,我检查了一下。

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

1
在asp.net Core 2.0的控制器中对我来说非常
有用

2
_userManager是什么?
NullVoxPopuli

6
在ASP.NET Core身份中,用户管理器是依赖注入提供的用于创建用户的服务。有关更多信息,请参阅文档
Ahmad

2
如何通过非异步方法实现呢?
T3.0

对我来说,返回null。为什么?
阿尔贝托·克拉迪奥(AlbertoCláudio)将于

22

还有另一种在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的控制器。没有两个等待就不会工作(不会编译);)


这需要使用身份
Fraze

1
什么是ApplicationUser?
迈克(Mike)

ApplicationUser通常从IdentityUser继承,因此它可以与另外的特性等进行扩展
Corgalore

20

我不得不说我很惊讶HttpContext在构造函数中为null。我确定是出于性能原因。已确认IPrincipal按如下所述使用确实会将其注入到构造函数中。它的作用基本上与接受的答案相同,但是采用的是界面化的方式。


对于任何发现此问题的人,都希望找到通用的“如何获得当前用户?”的答案您可以User直接从访问Controller.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代表用户和用户名。如果'Principal'听起来很奇怪,Wikipedia将为您提供安慰

重要的是要意识到,不管你得到它IHttpContextAccessor.HttpContext.UserControllerBase.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需要转换IPrincipalClaimsPrincipal。很好,因为我们知道在运行时100%就是这种类型的(因为这就是事实HttpContext.User)。我实际上很喜欢在构造函数中执行此操作,因为我已经确定IPrincipal 可以将ClaimsPrincipal

如果您要进行模拟,则只需直接创建一个ClaimsPrincipal并将其传递给takes即可IPrincipal

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


2
这使您可以在应用程序中的任意位置注入当前用户,这是一个很好的答案!
马查多

1
这行不通。我总是null为注射而努力IPrincipal。我还需要将临时服务添加为…GetService<IHttpContextAccessor>()?.HttpContext.User…(带有?),因为否则它将崩溃(GetService返回null)。
伊戈

您可以services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);作为HttpContext.User做为ClaimsPrincipal。
杰·泽洛斯

18

截至目前(2017年4月),似乎可以使用以下功能:

public string LoggedInUser => User.Identity.Name;

至少在 Controller


4
您不能将类型'System.Security.Principal.IIdentity'隐式转换为'string'。
Anthony Huang

3
字符串LoggedInUser = User.Identity.Name;
Alic W '18

5
作为以前从未见过=>运算符的人,它被称为“表达式主体定义”,并在本文档中进行了描述。以防万一像我这样的未来人在想。
内森·克莱门特

鉴于没有从IIdentity到的转换,因此您的代码可能无法在编辑之前进行编译string(如顶部注释中所述)。编辑只是解决了这个问题。我也不确定您是如何得出结论的(特别是因为“编辑”分数仅授予信誉低于2k的用户)。
fuglede

9

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

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

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


4

我的问题是将登录的用户作为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类上的公司导航属性的公司名称)的任何想法。没有找到包含导航属性的方法。
猎人纳尔逊

1

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

这对于重构 例如 您不希望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();
    }
}

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


0

服用IdentityUser也可以。这是当前的用户对象,并且可以检索用户的所有值。

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

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


0

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

我最喜欢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中间件)的配置。为了解决此问题,我建议手动设置“身份验证/授权”。添加了有关如何设置的简短概述。


0

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

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


-1

我有解决方案

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

1
尽管此代码可以回答问题,但提供有关如何以及为什么解决问题的其他上下文将提高答案的长期价值。
亚历山大
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.