如何在.NET Core Web API中获取当前用户(来自JWT Token)

问题描述 投票:21回答:5

经过大量的努力(以及许多tuturials,指南等)后,我设法设置了一个小的.NET Core REST Web API,当存储的用户名和密码有效时,Auth Controller会发出JWT令牌。

令牌将用户ID存储为子声明。

当方法使用Authorize注释时,我还设法设置Web API来验证这些令牌。

 app.UseJwtBearerAuthentication(...)

现在我的问题是:如何在我的控制器中(在Web API中)读取用户ID(存储在主题声明中)?

这基本上是这个问题(How do I get current user in ASP .NET Core),但我需要一个web api的答案。我没有UserManager。所以我需要从某个地方阅读主题索赔。

jwt asp.net-core-webapi asp.net-core-1.1
5个回答
14
投票

您可以使用此方法:

var email = User.FindFirst("sub")?.Value;

在我的情况下,我使用电子邮件作为一个独特的价值


26
投票

接受的答案对我不起作用。我不确定这是由我使用.NET Core 2.0还是由其他东西引起的,但看起来该框架将Subject Claim映射到NameIdentifier声明。所以,以下对我有用:

string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

请注意,这假定在JWT中设置了主题sub声明,其值是用户的ID。

默认情况下,.NET中的JWT身份验证处理程序会将JWT访问令牌的子声明映射到System.Security.Claims.ClaimTypes.NameIdentifier声明类型。 [Source]

还有一个discussion thread on GitHub,他们总结这种行为令人困惑。


7
投票

如果你使用Name存储ID

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Id.ToString())
                }),
    Expires = DateTime.UtcNow.AddDays(7),
    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};

在每种控制器方法中,您可以通过以下方式下载当前用户的ID:

var claimsIdentity = this.User.Identity as ClaimsIdentity;
var userId = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;

5
投票

似乎很多人都在关注这个问题,所以我想分享一些我学习的信息,因为我不久前问过这个问题。它使一些事情变得更加清晰(至少对我来说)并且不那么明显(对我来说就像.NET新手一样)。

正如MarcusHöglund在评论中提到的那样:

它应该与“web api”相同。在ASP.NET Core Mvc和Web Api中合并使用相同的控制器。

这绝对是真的,绝对正确。


因为它在.NET和.NET Core中都是一样的。

回到我不熟悉.NET Core,实际上是完整的.NET世界。重要的缺失信息是,在.NET和.NET Core中,所有身份验证都可以使用其ClaimsIdentity,ClaimsPrinciple和Claims.Properties修剪到System.Security.Claims命名空间。因此,它可以在.NET Core控制器类型(API和MVC或Razor或......)中使用,并且可以通过HttpContext.User访问。

所有教程都错过了一个重要的旁注。

因此,如果您开始使用.NET中的JWT令牌,请不要忘记对ClaimsIdentityClaimsPrincipleClaim.Properties充满信心。这就是全部。现在你知道了。 Heringer在其中一条评论中指出了这一点。


所有基于声明的身份验证中间件(如果正确实现)将使用在身份验证期间收到的声明填充HttpContext.User

据我所知,这意味着可以安全地信任HttpContext.User中的值。但是等一下,知道选择中间件时要注意什么。有许多不同的身份验证中间件已经可用(除了.UseJwtAuthentication())。

使用小型自定义扩展方法,您现在可以获得当前用户ID(更准确的主题声明)

 public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value; }

或者您使用Ateik答案中的版本。


但是等待:有一件奇怪的事情

接下来让我感到困惑的是:根据OpenID Connect规范,我正在寻找“sub”声明(当前用户)但却无法找到它。就像Honza Kalfus在答案中所做的那样。

为什么?

因为微软“有时”“有点”不同。或者至少他们会做更多(和意外)的事情。例如,原始问题中提到的官方Microsoft JWT Bearer身份验证中间件。 Microsoft决定在其所有官方身份验证中间件中转换声明(声明的名称)(出于兼容性原因,我不知道更详细)。

您将找不到“子”声明(尽管它是OpenID Connect指定的单个声明)。因为它被转换为these fancy ClaimTypes。这并非全是坏事,如果您需要将不同的声明映射到唯一的内部名称,它允许您添加映射。

您要么坚持使用Microsoft命名(并且必须记住,当您添加/使用非Microsoft中间件时)或者您发现如何为Microsoft中间件转换声明映射。

在JwtBearerAuthentication的情况下,它完成(在StartUp的早期或至少在添加中间件之前):

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

如果你想坚持主题声称的微软命名(不要打败我,如果Name是正确的映射,我现在不确定):

    public static string SubjectId(this ClaimsPrincipal user) { return user?.Claims?.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase))?.Value; }

请注意,其他答案使用更先进和更方便的FindFirst方法。虽然我的代码示例显示没有那些你可能应该与他们一起。

因此,您在HttpContext.User中存储和访问您的所有声明(通过一个名称或另一个名称)。


但我的令牌在哪里?

我不知道其他中间件,但JWT承载认证允许为每个请求保存令牌。但这需要激活(在StartUp.ConfigureServices(...)。

services
  .AddAuthentication("Bearer")
  .AddJwtBearer("Bearer", options => options.SaveToken = true);

然后可以通过访问实际的令牌(以其所有的神秘形式)作为字符串(或null)

HttpContext.GetTokenAsync("Bearer", "access_token")

这个方法有一个旧版本(这在.NET Core 2.2中适用于我,没有弃用警告)。

如果您需要解析并从此字符串中提取值,可能How to decode JWT token的问题会有所帮助。


好吧,我希望这个总结对你也有帮助。


0
投票

你可以这样做。

user.identity.name

© www.soinside.com 2019 - 2024. All rights reserved.