[英]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
. user
是null
。
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
功能,以便它通过持有UserID
的claim
获取用户?
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.