当前位置: 首页 > 知识库问答 >
问题:

如何获取.NET核心Web API中的当前用户(来自JWT令牌)

洪星文
2023-03-14

经过很多挣扎(以及大量的指导,指南等),我设法设置了一个小型的.NET Core REST Web API,当存储的用户名和密码有效时,使用身份验证控制器颁发JWT令牌。

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

我还设法设置了Web API,以便在方法使用Authorize注释时验证这些标记。

 app.UseJwtBearerAuthentication(...)

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

这基本上是这个问题(如何在ASP中获得当前用户。NET Core)但我需要一个Web api的答案。而且我没有UserManager。所以我需要从某个地方阅读主题声明。

共有3个答案

戎俊
2023-03-14

似乎很多人都在关注这个问题,所以我想分享一些我不久前问这个问题后所学到的更多信息。它让一些事情变得更加清晰(至少对我来说),而不是那么明显(对我来说是。NET新手)。

正如Marcus Höglund在评论中提到的:

在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令牌,不要忘记对ClaimsIdentity、ClaimsPrinciple和Claim.Properties也充满信心。都是关于这个的。现在你知道了。赫林格在其中一条评论中指出了这一点。

所有基于声明的认证中间件将(如果正确实现)填充< code>HttpContext。身份验证期间收到声明的用户。

据我所知,这意味着可以安全地信任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规范,我正在寻找“子”声明(当前用户),但找不到。就像Honza Kalfus在回答中做不到的。

为什么?

因为微软“有时候”“有点”与众不同。或者至少他们做了更多(意想不到的)事情。比如原问题中提到的官方微软JWT承载认证中间件。微软决定转换他们所有官方认证中间件中的声明(声明的名称)(出于兼容性原因,我不知道更多细节)。

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

要么您坚持使用Microsoft命名(并且在添加/使用非Microsoft中间件时必须注意这一点),要么您了解如何转换Microsoft中间件的声明映射。

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

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载体认证允许保存每个请求的令牌。但这需要激活(在< code >启动中。配置服务(...)。

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

然后可以通过以下方式访问字符串(或null)形式的实际令牌(以其所有隐含形式)

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

这种方法有一个旧版本(这在。NET Core 2.2无弃用警告)。

如果您需要从此字符串中解析和提取值,问题如何解码 JWT 令牌可能会有所帮助。

好吧,我希望该摘要对您有所帮助。

慕祯
2023-03-14

您可以使用此方法:

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

在我的例子中,我使用电子邮件作为唯一值

裴威
2023-03-14

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

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

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

默认情况下,中的JWT身份验证处理程序。NET会将JWT访问令牌的子声明映射到< code >系统。security . claims . claim types . name identifier 声明类型。[来源]

GitHub上还有一个讨论主题,他们得出结论认为这种行为令人困惑。

 类似资料:
  • IServiceProvider是一个具有单一方法的接口: 它用于创建已注册的类型的实例。NET Core原生DI容器。 可以通过调用IServiceCollection的方法来获得IServiceProvider自身的实例。它似乎是通过框架的实例神奇地调用的。 我想创建一个的实例,而不需要方法。我需要它来解决集成测试程序集中的依赖关系。在这种情况下有可能得到它吗?

  • 问题内容: 我在Linux上使用libnuma。我的线程应该知道它们正在运行的节点/核心。是否可以某种方式获取当前线程的节点/核心?我已经看过文档,但是没有找到这样的功能… 问题答案: 我找到了这个解决方案: 然后,如果需要cpu的节点,则可以使用numa.h:

  • 我有一个Spring Boot REST应用程序,它使用JWT令牌进行授权。我想使用AuthenticationPrincipal注释获取控制器中当前登录的用户。但如果我从loadUserByUsername返回自定义模型,并且auth停止工作,它总是返回null。我的模型实现了用户详细信息。 我试图扩展组织。springframework。安全果心用户详细信息。但我从JWTAuthenticat

  • 您好,我正在使用平均堆栈和jwt登录,我想限制一些数据只有userRole'admin'这是我的user.js 这就是我验证jwt令牌的方法 这是我希望使用verifyToken函数仅为'admin'角色限制的数据 我的问题是,我如何访问当前登录的用户信息,并仅使管理员可读的/特殊的

  • 我正在尝试通过刷新令牌和JWT在. NET Core 2.1中实现基于令牌的身份验证。 这就是我实施JWT令牌的方式: 启动.cs 令牌生成: 我正在给他回复正确的数值。 但是一分钟后,我在 Postman 中为授权设置了相同的令牌,它可以工作。如果令牌已过期,则不应过期。 我使用不记名令牌作为认证。 我做错了什么?需要方向。

  • 我已经创建了自定义中间件类来验证JWT令牌。我在