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



在经历了很多挣扎之后(以及很多略含型,指南等),我设法设置了一个小的.NET核心REST Web API,并在存储用户名和用户名时会发出JWT代币的auth Controller密码有效。

令牌将用户ID存储为sub索赔。

我还设法设置了Web API以验证这些令牌时,当一种方法使用授权注释时。

app.usejwtbearerauthentication(...)

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

基本上是这个问题(如何在ASP .NET Core中获取当前用户),但是我需要Web API的答案。而且我没有Usermanager。因此,我需要从某个地方阅读主题主张。

接受的答案对我不起作用。我不确定这是我使用.NET Core 2.0或其他其他内容引起的,但是看起来该框架将主题声称映射到名称识别符声明。因此,以下对我有用:

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

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

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

github上还有一个讨论线程,他们结论这种行为令人困惑。

您可以使用此方法:

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

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

似乎很多人都在看这个问题,所以我想分享一些我在一段时间问这个问题以来就学到的更多信息。这使一些事情变得更加清楚(至少对我来说)并不那么明显(对我来说是.net newbie)。

AS MarcusHöglund评论中提到的:

对于" Web API"。在ASP.NET Core MVC和Web API中合并以使用相同的控制器。

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


因为在.NET和.NET核心上都是相同的。

Back比我是新手核心的新手,实际上是完整的.NET世界。重要的缺失信息是,在.NET和.NET核心中,所有身份验证都可以将其修剪到系统。Security.Security.claims命名空间及其索赔,索赔要求和索赔。因此,它都用于两种.NET核心控制器类型(API和MVC或Razor或...)中,并且可以通过HttpContext.User访问。

一个重要的旁注

因此,如果您开始使用.NET中的JWT令牌做某事,请不要忘记也对索赔的索赔,索赔要求和索赔。就是这样。现在你知道了。它由 Heringer 在其中一个评论中指出。


所有基于索赔的身份验证中间Wares(如果正确实施)将填充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 Spec,我正在寻找" Sub"声明(当前用户),但找不到它。像 Honza Kalfus 无法在他的回答中做。

为什么?

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

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

您要么坚持使用Microsoft命名(并且要介意添加/使用非Microsoft Mifdredware时),要么找出如何将Microsoft Middleware的索赔映射。

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

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

如果您想坚持Microsoft命名主题主张(请勿击败我,我不确定现在名称是正确的映射):

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

请注意,其他答案使用更高级和更方便的Findfirst方法。尽管我的代码示例显示了它,但您可能应该随身携带。

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


但是我的令牌在哪里?

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

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

然后可以通过

访问字符串(或null)的实际令牌(在所有的神秘形式中)
HttpContext.GetTokenAsync("Bearer", "access_token")

有这种方法的较旧版本(这在.net core 2.2中对我有用,而无需弃用警告)。

如果您需要从该字符串中解析和提取值,则可能会如何解码JWT Token帮助。


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

如果您使用NameID存储在此处:

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;

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

var email = string.Empty;
if (HttpContext.User.Identity is ClaimsIdentity identity)
{
    email = identity.FindFirst(ClaimTypes.Name).Value;
}

在我的情况下,我将sipertypes.name设置为唯一用户电子邮件,然后在jwt token生成之前:

claims.Add(new Claim(ClaimTypes.Name, user.UserName));

然后,我将唯一的用户ID存储到sipertypes.nameidentifier:

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.Identity.name

我使用以下代码在.NET Core 5 Web API

中使用以下代码
User.Claims.First(x => x.Type == "id").Value;

asp.net核心身份获取用户ID

 public async Task<IActionResult> YourMethodName()
{
    var userId =  User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
    var userName =  User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
    ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
    string userEmail = applicationUser?.Email; // will give the user's Email
}

.net核心身份获取用户ID

 public static class ClaimsPrincipalExtensions
{
    public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
    {
        if (principal == null)
            throw new ArgumentNullException(nameof(principal));
        var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
        if (typeof(T) == typeof(string))
        {
            return (T)Convert.ChangeType(loggedInUserId, typeof(T));
        }
        else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
        {
            return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
        }
        else
        {
            throw new Exception("Invalid type provided");
        }
    }
    public static string GetLoggedInUserName(this ClaimsPrincipal principal)
    {
        if (principal == null)
            throw new ArgumentNullException(nameof(principal));
        return principal.FindFirstValue(ClaimTypes.Name);
    }
    public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
    {
        if (principal == null)
            throw new ArgumentNullException(nameof(principal));
        return principal.FindFirstValue(ClaimTypes.Email);
    }
}

最新更新