简体   繁体   English

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

[英]JWT Authentication - UserManager.GetUserAsync returns null

In AuthController when authenticating I create a few Claims - UserID is one of them.AuthController进行身份验证时,我创建了一些声明- UserID就是其中之一。

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

When Angular app makes request I am able to fetch UserID in another controller当 Angular 应用发出请求时,我能够在另一个 controller 中获取UserID

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

The ControllerBase.User instance holds .Identity object which in turn holds Claims collection. ControllerBase.User实例包含.Identity object,后者又包含Claims集合。

  • Identity.IsAuthenticated equals True . Identity.IsAuthenticated等于True

  • Identity.Name holds admin string (name of the relevant user). Identity.Name包含admin字符串(相关用户的名称)。

If I try to fetch user like this:如果我尝试像这样获取用户:

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

the user is null . usernull

Perhaps, I forgot to add some extra claim?也许,我忘了添加一些额外的声明?

Or maybe, once I'm using JWT - I should override the default UserManager functionality so it fetches user by claim which holds UserID ?或者,一旦我使用 JWT - 我应该覆盖默认的UserManager功能,以便它通过持有UserIDclaim获取用户?

Or maybe there's a better approach?或者也许有更好的方法?


Additional info:附加信息:

The Identity is registered as follows Identity注册如下

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

ApplicationUser.Id field is of bigint (or in C# of long ) type ApplicationUser.Id字段是bigint (或 C# 的long )类型

Also, I create users in EF Seed Data with UserManager which is resolved using ServiceProvider此外,我使用 UserManager 在EF Seed Data中创建用户,该用户使用ServiceProvider解析

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

UserManager.GetUserAsync internally uses UserManager.GetUserId to retrieve the user id of the user which is then used to query the object from the user store (ie your database). UserManager.GetUserAsync内部使用UserManager.GetUserId来检索用户的用户 ID,然后该用户 ID 用于从用户存储(即您的数据库)查询对象。

GetUserId basically looks like this: GetUserId基本上如下所示:

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

So this returns the claim value of Options.ClaimsIdentity.UserIdClaimType .所以这将返回Options.ClaimsIdentity.UserIdClaimType的声明值。 Options is the IdentityOptions object that you configure Identity with. Options是您用来配置 Identity 的IdentityOptions对象 By default the value of UserIdClaimType is ClaimTypes.NameIdentifier , ie "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" .默认情况下, UserIdClaimType值为ClaimTypes.NameIdentifier ,即"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"

So when you try to use UserManager.GetUserAsync(HttpContext.User) , where that user principal has a UserID claim, the user manager is simply looking for a different claim.因此,当您尝试使用UserManager.GetUserAsync(HttpContext.User) ,该用户主体具有UserID声明,用户管理器只是在寻找不同的声明。

You can fix this by either switchting to the ClaimTypes.NameIdentifier :您可以通过切换到ClaimTypes.NameIdentifier来解决此ClaimTypes.NameIdentifier

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

Or you configure Identity properly so it will use your UserID claim type:或者您正确配置 Identity,以便它使用您的UserID声明类型:

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

When you create Claims , you just need to do like me当你创建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)
 };

For me it was a tutorial that gave bad advice, it assigned a "general description" to the "sub" claim of the JWT-token.对我来说,这是一个给出错误建议的教程,它为 JWT 令牌的“子”声明分配了“一般描述”。

A claim entry for ClaimTypes.NameIdentifier is automatically created from the "sub" claim in the JWT auth library, so it ended up double. ClaimTypes.NameIdentifier的声明条目是从 JWT 授权库中的“子”声明自动创建的,因此它最终是双重的。 One with incorrect value and one with correct.一个值不正确,一个值正确。

This was the incorrect implementation:这是不正确的实现:

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

This is the correct implementation:这是正确的实现:

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

How the claims look with the correct implementation:正确实施后声明的外观:

理赔

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

相关问题 _userManager.GetUserAsync(User) 返回 null - _userManager.GetUserAsync(User) returns null 为什么UserManager.GetUserAsync在使用JWT时返回null? - Why does UserManager.GetUserAsync return null when using JWT? UserManager.GetUserAsync(User)是否可以在具有Authorize属性的类中返回null? - Can UserManager.GetUserAsync(User) return null in a class with Authorize attribute? 为什么 UserManager.GetUserAsync 不返回? - why does UserManager.GetUserAsync not return? 当照片不存在时,UserManager.GetUserAsync(User).Result.ProfilePicture 失败 - UserManager.GetUserAsync(User).Result.ProfilePicture Failing when Photo does not exists 为什么我们每个动作方法都必须调用await _userManager.GetUserAsync(User)? - Why do we have to invoke await _userManager.GetUserAsync(User) per action method? Angular 客户端,WebAPI 服务器 - userManager.GetUserAsync() 方法时的 cookie 问题 - Angular client, WebAPI server - cookie issue when userManager.GetUserAsync() method 尽管使用UserManager扩展方法,FindAsync仍返回null - FindAsync returns null despite UserManager extension method 身份 userManager.FindByNameAsync() 返回 null 值 - Identity userManager.FindByNameAsync() returns null value UserManager.FindByEmailAsync 返回 null,但该用户存在于数据库中 - UserManager.FindByEmailAsync returns null, but the user exists in the database
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM