JWT 身份验证 - UserManager.GetUserAsync returns 空

JWT Authentication - UserManager.GetUserAsync returns null

AuthController 进行身份验证时,我创建了一些 声明 - UserID 就是其中之一。

...
Subject = new ClaimsIdentity(new[]
{
  new Claim(ClaimTypes.Name, user.UserName),
  new Claim("UserID", user.Id.ToString()),
})

当 Angular 应用发出请求时,我可以在另一个控制器中获取 UserID

Claim claimUserId = User.Claims.SingleOrDefault(c => c.Type == "UserID");

ControllerBase.User 实例包含 .Identity 对象,而对象又包含 Claims 集合。

如果我尝试这样获取用户:

var user = await UserManager.GetUserAsync(HttpContext.User)

usernull

也许,我忘了添加一些额外的声明?

或者,一旦我使用 JWT - 我应该覆盖默认的 UserManager 功能,以便它通过 claim 获取用户,其中包含 UserID?

或者也许有更好的方法?


附加信息:

Identity注册如下

services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<AppDbContext>()
    .AddDefaultTokenProviders();

ApplicationUser.Id 字段是 bigint(或在 C# 中的 long)类型

此外,我在 EF Seed Data 中使用 UserManager 创建用户,使用 ServiceProvider

解析
_userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
    ...
        adminUser.PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(adminUser, "123qwe");
        _userManager.CreateAsync(adminUser);

UserManager.GetUserAsync internally uses UserManager.GetUserId 检索用户的用户 ID,然后用于从用户存储(即您的数据库)中查询对象。

GetUserId 基本上是这样的:

public string GetUserId(ClaimsPrincipal principal)
{
    return principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType);
}

所以这个 returns 的声明值 Options.ClaimsIdentity.UserIdClaimTypeOptions 是您用来配置身份的 IdentityOptions object。默认情况下 UserIdClaimType 的值为 ClaimTypes.NameIdentifier,即 "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier".

因此,当您尝试使用 UserManager.GetUserAsync(HttpContext.User) 时,该用户主体有一个 UserID 声明,用户管理器只是在寻找一个不同的声明。

您可以通过切换到 ClaimTypes.NameIdentifier:

来解决这个问题
new ClaimsIdentity(new[]
{
    new Claim(ClaimTypes.Name, user.UserName),
    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
})

或者您正确配置身份,以便它将使用您的 UserID 声明类型:

// in Startup.ConfigureServices
services.AddIdentity(options => {
    options.ClaimsIdentity.UserIdClaimType = "UserID";
});

当您创建声明时,您只需执行以下操作:

 List<Claim> claims = new()
 {
    new Claim(ClaimTypes.NameIdentifier, user.Id),  // This line is important.
    new Claim(ClaimTypes.Email, user.Email),
    new Claim(JwtRegisteredClaimNames.Jti, jti)
 };