如何在 .NET Core Web API 中获取当前用户(来自 JWT 令牌) [英] How do I get current user in .NET Core Web API (from JWT Token)

查看:149
本文介绍了如何在 .NET Core Web API 中获取当前用户(来自 JWT 令牌)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过大量的努力(以及大量的教程、指南等),我设法设置了一个小型 .NET Core REST Web API,其中一个身份验证控制器在存储的用户名和密码有效时发出 JWT 令牌.

After a lot of struggling (and a lot of tuturials, guides, etc) I managed to setup a small .NET Core REST Web API with an Auth Controller issuing JWT tokens when stored username and password are valid.

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

The token stores the user id as sub claim.

我还设法设置了 Web API 以在方法使用 Authorize 注释时验证这些令牌.

I also managed to setup the Web API to validate those tokens when a method uses the Authorize annotation.

 app.UseJwtBearerAuthentication(...)

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

Now my question: How do I read the user id (stored in the subject claim) in my controllers (in a Web API)?

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

It is basically this question (How do I get current user in ASP .NET Core) but I need an answer for a web api. And I do not have a UserManager. So I need to read the subject claim from somewhere.

推荐答案

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

It seems a lot of people are looking at this question so I would like to share some more information I learned since I asked the question a while back. It makes some things more clear (at least for me) and wasn't so obvious (for me as .NET newbie).

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

web api"应该是一样的.在 ASP.NET Core Mvc 和 Web Api 合并使用相同的控制器.

It should be the same for "web api"..In ASP.NET Core Mvc and Web Api are merged to use the same controller.

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

That's definitely true and absolutely correct.

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

Because it is all the same across .NET and .NET Core.

比我刚接触 .NET Core 和实际上完整的 .NET 世界时还早.缺少的重要信息是,在 .NET 和 .NET Core 中,所有身份验证都可以缩减为 System.Security.Claims 命名空间及其 ClaimsIdentity、ClaimsPrinciple 和 Claims.Properties.因此,它可用于 .NET Core 控制器类型(API 和 MVC 或 Razor 或 ...),并可通过 HttpContext.User 访问.

Back than I was new to .NET Core and actually the full .NET world. The important missing information was that in .NET and .NET Core all the authentication can be trimmed down to System.Security.Claims namespace with its ClaimsIdentity, ClaimsPrinciple and Claims.Properties. And therefore it is used in both .NET Core controller types (API and MVC or Razor or ...) and is accessible via HttpContext.User.

一个重要的旁注,所有教程都遗漏了.

An important side note all of the tutorials missed to tell.

因此,如果您开始在 .NET 中使用 JWT 令牌做某事,请不要忘记对 ClaimsIdentityClaimsPrincipleClaim.Properties.就是这样.现在你知道了.Heringer 在其中一条评论中指出了这一点.

So if you start doing something with JWT tokens in .NET don't forget to also get confident with ClaimsIdentity, ClaimsPrinciple and Claim.Properties. It's all about that. Now you know it. It was pointed out by Heringer in one of the comments.

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

ALL the claim based authentication middlewares will (if correctly implemented) populate the HttpContext.User with the claims received during authentication.

据我所知,这意味着可以安全地信任 HttpContext.User 中的值.但请稍等,了解在选择中间件时要注意什么.有很多不同的认证中间件已经可用(除了 .UseJwtAuthentication()).

As far as I understand now this means one can safely trust on the values in the HttpContext.User. But wait a bit to know what to mind when selecting middleware. There are a lot of different authentication middleware already available (in addition to .UseJwtAuthentication()).

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

With small custom extension methods you can now get the current user id (more accurate the subject claim) like that

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

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

Or you use the version in the answer of Ateik.

BUT WAIT:有一件奇怪的事情

接下来让我困惑的事情是:根据 OpenID Connect 规范,我正在寻找子"声明(当前用户)但找不到它.就像 Honza Kalfus 在他的回答中做不到一样.

The next thing that confused me back than: according to the OpenID Connect spec I was looking for "sub" claim (the current user) but couldn't find it. Like Honza Kalfus couldn't do in his answer.

为什么?

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

Because Microsoft is "sometimes" "a bit" different. Or at least they do a bit more (and unexpected) things. For example the official Microsoft JWT Bearer authentication middleware mentioned in the original question. Microsoft decided to convert claims (the names of the claims) in all of their official authentication middleware (for compatibility reasons I don't know in more detail).

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

You won't find a "sub" claim (though it is the single one claim specified by OpenID Connect). Because it got converted to these fancy ClaimTypes. It's not all bad, it allows you to add mappings if you need to map different claims into a unique internal name.

要么坚持使用 Microsoft 命名(并且在添加/使用非 Microsoft 中间件时必须记住这一点),要么找出如何为 Microsoft 中间件转换声明映射.

Either you stick with the Microsoft naming (and have to mind that when you add/use a non Microsoft middleware) or you find out how to turn the claim mapping of for the Microsoft middleware.

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

In case of the JwtBearerAuthentication it is done (do it early in StartUp or at least before adding the middleware):

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

如果您想坚持使用 Microsoft 命名主题声明(不要打败我,我现在不确定 Name 是否是正确的映射):

If you want to stick with the Microsoft namings the subject claim (don't beat me, I am not sure right now if Name is the correct mapping):

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

请注意,其他答案使用更高级和更方便的方式 FindFirst 方法.虽然我的代码示例显示它没有那些你可能应该使用它们.

Note that the other answers use the more advanced and way more convenient FindFirst method. Though my code samples show it without those you may should go with them.

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

So all your claims are stored and accessible (via one name or the other) in the HttpContext.User.

但是我的代币呢?

我不知道其他中间件,但 JWT 不记名身份验证允许为每个请求保存令牌.但这需要激活(在 StartUp.ConfigureServices(...) 中).

I don't know for the other middleware but the JWT Bearer Authentication allows to save the token for each request. But this needs to be activated (in StartUp.ConfigureServices(...).

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

然后可以通过

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

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

There has been an older version of this method (this works for me in .NET Core 2.2 without deprecated warning).

如果您需要从这个字符串中解析和提取值可能会出现如何解码 JWT 令牌的问题 有帮助.

If you need to parse and extract values from this string may the question How to decode JWT token helps.

好吧,我希望总结也能帮到你.

Well, I hope that summary helps you too.

这篇关于如何在 .NET Core Web API 中获取当前用户(来自 JWT 令牌)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆