【问题标题】:How do I get current user in .NET Core Web API (from JWT Token)如何在 .NET Core Web API 中获取当前用户(来自 JWT 令牌)
【发布时间】:2018-02-17 03:08:44
【问题描述】:

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

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

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

 app.UseJwtBearerAuthentication(...)

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

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

【问题讨论】:

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

标签: jwt asp.net-core-webapi asp.net-core-1.1


【解决方案1】:

你可以使用这个方法:

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

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

【讨论】:

  • 谢谢,应该被标记为接受的答案!对于用户名:User.Identity.Name。 User 是 Microsoft.AspNetCore.Mvc.ControlerBase 的一个属性,它的类型是 System.Security.Claims.ClaimsPrincipal。刚刚添加。
  • 另一种方式可能是:string sub = HttpContext?.User.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;
  • 或 User.FindFirstValue(ClaimTypes.Name)
  • 请注意,如果您不使用[Authorize] 属性,则User 可能是一种空用户,其中User.Identity.IsAuthenticated 为假。所以要小心。
【解决方案2】:

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

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

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

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

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

【讨论】:

【解决方案3】:

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

【讨论】:

  • var userId = User.Identity.Name
【解决方案4】:

你可以这样做。

用户.身份.名称

【讨论】:

    【解决方案5】:

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

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

    “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 在其中一个 cmets 中指出了这一点。


    所有基于声明的身份验证中间件(如果正确实施)将使用身份验证期间收到的声明填充 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 规范,我正在寻找“子”声明(当前用户)但找不到它。就像 Honza Kalfus 在他的回答中无法做到的那样。

    为什么?

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

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

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

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

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    

    如果您想坚持使用 Microsoft 命名主题声明(不要打败我,我现在不确定 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);
    

    然后可以通过

    访问作为字符串(或空值)的实际令牌(以所有神秘的形式)
    HttpContext.GetTokenAsync("Bearer", "access_token")
    

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

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


    嗯,我希望这个总结也能帮助到你。

    【讨论】:

    • 让实际令牌中的声明与控制器中可用的声明完全匹配非常有意义。我认为您的答案应该从 Startup.cs 开始: JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();然后在 Controller.cs 中: var userSub = User.FindFirst("sub")?.Value;这是唯一完全解释问题的答案,而不仅仅是链接到 GitHub 上一个令人困惑的线程。干得好。
    • 我希望我早点读过这个答案。浪费了一个小时来了解 .Net 如何更改声明类型。我明确地将声明命名为“sub”,但我只是想不通为什么“jti”有效,但“sub”无效。这会让很多 .Net Core 中 JWT 的新手感到困惑。您的回答澄清了这一点。
    【解决方案6】:

    我使用了 HttpContext,效果很好:

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

    【讨论】:

      【解决方案7】:

      在我的例子中,我在生成 JWT 令牌之前将 ClaimTypes.Name 设置为唯一的用户电子邮件:

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

      然后我将唯一的用户 ID 存储到 ClaimTypes.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);
              }
      

      【讨论】:

        【解决方案8】:

        我在 .net core 5 web api 中使用以下代码工作

        User.Claims.First(x => x.Type == "id").Value;
        

        【讨论】:

          【解决方案9】:

          asp.net core identity 获取用户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 core identity 获取用户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);
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2021-07-22
            • 2019-01-04
            • 2020-06-28
            • 1970-01-01
            • 2021-03-26
            • 2021-12-16
            • 2018-07-18
            • 2020-06-12
            • 2021-06-27
            相关资源
            最近更新 更多