繁体   English   中英

JWT 身份验证 - UserManager.GetUserAsync 返回 null

[英]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 应用发出请求时,我能够在另一个 controller 中获取UserID

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

ControllerBase.User实例包含.Identity object,后者又包含Claims集合。

  • Identity.IsAuthenticated等于True

  • Identity.Name包含admin字符串(相关用户的名称)。

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

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

usernull

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

或者,一旦我使用 JWT - 我应该覆盖默认的UserManager功能,以便它通过持有UserIDclaim获取用户?

或者也许有更好的方法?


附加信息:

Identity注册如下

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

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

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

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

UserManager.GetUserAsync内部使用UserManager.GetUserId来检索用户的用户 ID,然后该用户 ID 用于从用户存储(即您的数据库)查询对象。

GetUserId基本上如下所示:

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

所以这将返回Options.ClaimsIdentity.UserIdClaimType的声明值。 Options是您用来配置 Identity 的IdentityOptions对象 默认情况下, UserIdClaimType值为ClaimTypes.NameIdentifier ,即"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"

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

您可以通过切换到ClaimTypes.NameIdentifier来解决此ClaimTypes.NameIdentifier

new ClaimsIdentity(new[]
{
    new Claim(ClaimTypes.Name, user.UserName),
    new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
})

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

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

当你创建Claims 时,你只需要像我一样做

 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)
 };

对我来说,这是一个给出错误建议的教程,它为 JWT 令牌的“子”声明分配了“一般描述”。

ClaimTypes.NameIdentifier的声明条目是从 JWT 授权库中的“子”声明自动创建的,因此它最终是双重的。 一个值不正确,一个值正确。

这是不正确的实现:

   private Claim[] CreateClaims(IdentityUser user)
    {
        return new[] {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Sub, _configuration["Jwt:Subject"]),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
        };
    }

这是正确的实现:

private Claim[] CreateClaims(IdentityUser user)
{
    return new[] {
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.Email, user.Email),
        new Claim(JwtRegisteredClaimNames.Sub, user.Id),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
    };
}

正确实施后声明的外观:

理赔

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM