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

如何获取.NET核心Web API中的当前用户(来自JWT令牌),jwt,asp.net-core-webapi,asp.net-core-1.1,Jwt,Asp.net Core Webapi,Asp.net Core 1.1,经过大量的努力(以及大量的教程、指南等),我成功地建立了一个小型的.NET Core REST Web API,当存储的用户名和密码有效时,Auth控制器会发出JWT令牌 令牌将用户id存储为子声明 我还设法设置了Web API,以便在方法使用Authorize注释时验证这些令牌 app.UseJwtBearerAuthentication(...) app.UseJwtBearerAuthentication(…) 现在我的问题是: 如何读取控制器(在Web API中)中的用户id(存储在主题

经过大量的努力(以及大量的教程、指南等),我成功地建立了一个小型的.NET Core REST Web API,当存储的用户名和密码有效时,Auth控制器会发出JWT令牌

令牌将用户id存储为子声明

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

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


基本上就是这个问题(),但我需要一个web api的答案。我没有用户管理器。因此,我需要从某处阅读主题声明。

您可以使用以下方法:

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

就我而言,我使用电子邮件作为唯一值

接受的答案对我不起作用。我不确定这是由于我使用.NETCore2.0还是其他原因造成的,但看起来框架将主题声明映射到了NameIdentifier声明。因此,以下几点对我很有用:

string userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
注意,这假设在JWT中设置了Subject
sub
声明,其值是用户的id

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


还有一个原因是他们认为这种行为令人困惑。

如果您使用
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;
您可以使用

User.Identity.Name


似乎很多人都在关注这个问题,所以我想与大家分享一下我在前一段时间问这个问题后学到的更多信息。 它让一些事情变得更清楚(至少对我来说),而且不那么明显(对我这个.NET新手来说)

正如《评论》中提到的马库斯·霍格隆德(Marcus Höglund):

“web api”应该是相同的。在ASP.NET中,核心Mvc和web api合并使用相同的控制器

这绝对正确


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

那时我还不熟悉.NET核心,实际上是整个.NET世界。缺少的重要信息是,在.NET和.NET Core中,所有身份验证都可以通过其ClaimsEntity、ClaimsPrinciple和ClaimsPrinciple.Properties缩减到名称空间。因此,它在.NET核心控制器类型(API和MVC或Razor或…)中都使用,并且可以通过
HttpContext.User
访问

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

因此,如果您开始在.NET中使用JWT令牌做一些事情,请不要忘记也要对和充满信心。都是为了这个。现在你知道了。赫林格在一篇评论中指出了这一点


所有基于声明的身份验证中间件(如果正确实现)将使用身份验证期间收到的声明填充
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”声明(当前用户),但找不到它。就像霍扎·卡尔福斯在回答问题时做不到的那样

为什么?

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

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

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

对于JWTBeareAuthentication,它已经完成(在启动早期或至少在添加中间件之前完成):

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

请注意,其他答案使用更先进、更方便的方法。虽然我的代码示例没有这些示例,但您可能应该使用它们

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


但我的代币在哪里?

我不知道其他中间件的情况,但是JWT承载身份验证允许为每个请求保存令牌。但这需要被激活(在
StartUp.ConfigureServices(…
)中)

然后可以通过

HttpContext.GetTokenAsync("Bearer", "access_token")
此方法有一个较旧的版本(这在.NET Core 2.2中适用,没有不推荐的警告)

如果您需要从这个字符串中解析和提取值,这个问题可能会有所帮助



好的,我希望这个总结也能帮助您。

我使用了HttpContext,它运行良好:

var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
    email = identity.FindFirst(ClaimTypes.Name).Value;
}
在我的情况下,我设定了索赔类型
services
  .AddAuthentication("Bearer")
  .AddJwtBearer("Bearer", options => options.SaveToken = true);
HttpContext.GetTokenAsync("Bearer", "access_token")
var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
    email = identity.FindFirst(ClaimTypes.Name).Value;
}
claims.Add(new Claim(ClaimTypes.Name, user.UserName));
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
int GetLoggedUserId()
        {
            if (!User.Identity.IsAuthenticated)
                throw new AuthenticationException();

            string userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

            return int.Parse(userId);
        }
User.Claims.First(x => x.Type == "id").Value;