自定义 IdentityUser 的 CreateUserIdenityAsync returns "UserId not found" 异常
CreateUserIdenityAsync returns "UserId not found" exception for custom IdentityUser
我正在关注 bitoftech tutorial 关于使用 JWT 创建基于身份和角色的声明。我的应用程序用户是具有 int PK 的自定义用户 table。
目前,GenerateUserIdentityAsync 方法只是 returns 一个奇怪的 UserId not found
错误。这是我的代码:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
和 User
实体中的实现:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType)
{
//error on this line: CreateIdentityAsync throws error
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
return userIdentity;
}
我的 UserManager class 是这样定义的:
public class AppUserManager : UserManager<User, int>
奇怪的是,当我调试时,GenerateIdentityAsync
中的实例 this
确实有一个 UserId
属性,但基础只有一个 id
我想知道这是不是出错的地方? (听起来不对)
我正在查看 source code(第 80 行),但我无法弄清楚异常被抛出的位置。
抛出的确切异常是:
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.InvalidOperationException:
UserId not found.
而且 stack trace 对我来说也没什么帮助
如何找出 UserId 不可用的原因/位置?
模式详情:
我的GrantResourceOwnerCredentials()
:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null) // this is NOT null
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
// this line fails
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
还有 ApplicationUser
(在我的例子中,就是 User
)
public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public int UserId { get; set; }
public string Fullname { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
}
正如您在调试时发现的那样 IdentityUser
有一个 Id
,在您的情况下它代表用户的 ID。
您需要从 User
class 中删除 UserId
,使用 IdentityUser
中的基础 Id
并重命名 UserId
自定义用户中的列 table 到 Id
.
您在 User
class 中的任何属性都需要在数据库中的用户 table 中也有匹配的列。如果不是,那么对于不匹配的属性,您将得到相同的错误。
这意味着 Fullname
、Address
和 ContactNumber
必须在 AspNetUsers
table 中具有匹配的列名称,否则您将得到相同的错误对于这些属性也是如此。
您的 ApplicationUser class 是什么样子的?
这个方法在你的应用程序中是什么样子的?
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){}
Taiseer 关于 GrantResourceOwnerCredentials 的评论是:
"we are building an identity for the logged in user, this identity
will contain all the roles and claims for the authenticated user"
我必须将 ClaimTypes.NameIdentifier 添加到 ClaimsIdentity 才能解决类似问题。这是我的 GrantResourceOwnerCredentials 方法的重要部分:
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
您的 User
中同时具有 UserId
和 Id
属性 class - Id
继承自 IdentityUser
。问题是您可能将 UserId
配置为 User
.
的主键
您得到的异常是在 line 97 UserManager.GetSecurityStampAsync
的 ClaimsIdentityFactory.CreateAsync
方法中抛出的。如您所见,user.Id
用于检索安全戳。
如果你往里面看 UserManager.GetSecurityStampAsync
你会发现你得到的异常恰好在这里抛出:
public virtual async Task<string> GetSecurityStampAsync(TKey userId)
{
ThrowIfDisposed();
var securityStore = GetSecurityStore();
var user = await FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,
userId));
}
return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture();
}
因此,从 User
class 中删除 UserId
属性 并开始使用 Id
(继承自 IdentityUser
)。
我遇到了完全相同的问题。在头痛了很久之后,我可以解决真正的问题。
当您将 User class 的 Id 属性 的数据类型更改为字符串时,Guid().ToString() 将分配给构造函数中的 Id 属性 并保存相同的值到数据库,Identity 使用该值检索用户详细信息。
但是,如果您将 Id 属性 的数据类型更改为 int 并且没有在构造函数中为该 属性 提供值,Identity 仍会尝试通过以下方式检索用户详细信息使用默认的 int 值 (0) 这会导致抛出消息 "System.InvalidOperationException: UserId not found".
我通过 command.ExecuteScalar() 从数据库中检索值并将值分配给 user.Id 来解决这个问题。
希望这会对面临类似问题的人有所帮助。
服务器=xxxx;初始目录=xxxx;;用户 ID=xxxx_user;密码=xxxx;其中用户 ID 中有一个 space 而不是
服务器=xxxx;初始目录=xxxx;;用户ID=xxxx_user;密码=xxxx;错误的!!!!!!!
请参阅 https://www.connectionstrings.com/sql-azure/ 进行确认。
在我的例子中,发生这个错误是因为我在帐户控制器的登录方法中添加了另一个 SignInManager(以添加角色为例)。它在同一方法中执行 SignInManager.PasswordSignInAsync 和 SignInManager.SignInAsync 并且这两次调用 ApplicationUser 中的 GenerateUserIdentityAsync(...) ,第一次成功,第二次给了我异常 "UserId"未找到。
我正在关注 bitoftech tutorial 关于使用 JWT 创建基于身份和角色的声明。我的应用程序用户是具有 int PK 的自定义用户 table。
目前,GenerateUserIdentityAsync 方法只是 returns 一个奇怪的 UserId not found
错误。这是我的代码:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
和 User
实体中的实现:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType)
{
//error on this line: CreateIdentityAsync throws error
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
return userIdentity;
}
我的 UserManager class 是这样定义的:
public class AppUserManager : UserManager<User, int>
奇怪的是,当我调试时,GenerateIdentityAsync
中的实例 this
确实有一个 UserId
属性,但基础只有一个 id
我想知道这是不是出错的地方? (听起来不对)
我正在查看 source code(第 80 行),但我无法弄清楚异常被抛出的位置。
抛出的确切异常是:
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.InvalidOperationException:
UserId not found.
而且 stack trace 对我来说也没什么帮助
如何找出 UserId 不可用的原因/位置?
模式详情:
我的GrantResourceOwnerCredentials()
:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null) // this is NOT null
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
// this line fails
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
还有 ApplicationUser
(在我的例子中,就是 User
)
public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public int UserId { get; set; }
public string Fullname { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
}
正如您在调试时发现的那样 IdentityUser
有一个 Id
,在您的情况下它代表用户的 ID。
您需要从 User
class 中删除 UserId
,使用 IdentityUser
中的基础 Id
并重命名 UserId
自定义用户中的列 table 到 Id
.
您在 User
class 中的任何属性都需要在数据库中的用户 table 中也有匹配的列。如果不是,那么对于不匹配的属性,您将得到相同的错误。
这意味着 Fullname
、Address
和 ContactNumber
必须在 AspNetUsers
table 中具有匹配的列名称,否则您将得到相同的错误对于这些属性也是如此。
您的 ApplicationUser class 是什么样子的? 这个方法在你的应用程序中是什么样子的?
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){}
Taiseer 关于 GrantResourceOwnerCredentials 的评论是:
"we are building an identity for the logged in user, this identity will contain all the roles and claims for the authenticated user"
我必须将 ClaimTypes.NameIdentifier 添加到 ClaimsIdentity 才能解决类似问题。这是我的 GrantResourceOwnerCredentials 方法的重要部分:
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
您的 User
中同时具有 UserId
和 Id
属性 class - Id
继承自 IdentityUser
。问题是您可能将 UserId
配置为 User
.
您得到的异常是在 line 97 UserManager.GetSecurityStampAsync
的 ClaimsIdentityFactory.CreateAsync
方法中抛出的。如您所见,user.Id
用于检索安全戳。
如果你往里面看 UserManager.GetSecurityStampAsync
你会发现你得到的异常恰好在这里抛出:
public virtual async Task<string> GetSecurityStampAsync(TKey userId)
{
ThrowIfDisposed();
var securityStore = GetSecurityStore();
var user = await FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,
userId));
}
return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture();
}
因此,从 User
class 中删除 UserId
属性 并开始使用 Id
(继承自 IdentityUser
)。
我遇到了完全相同的问题。在头痛了很久之后,我可以解决真正的问题。 当您将 User class 的 Id 属性 的数据类型更改为字符串时,Guid().ToString() 将分配给构造函数中的 Id 属性 并保存相同的值到数据库,Identity 使用该值检索用户详细信息。
但是,如果您将 Id 属性 的数据类型更改为 int 并且没有在构造函数中为该 属性 提供值,Identity 仍会尝试通过以下方式检索用户详细信息使用默认的 int 值 (0) 这会导致抛出消息 "System.InvalidOperationException: UserId not found".
我通过 command.ExecuteScalar() 从数据库中检索值并将值分配给 user.Id 来解决这个问题。 希望这会对面临类似问题的人有所帮助。
服务器=xxxx;初始目录=xxxx;;用户 ID=xxxx_user;密码=xxxx;其中用户 ID 中有一个 space 而不是 服务器=xxxx;初始目录=xxxx;;用户ID=xxxx_user;密码=xxxx;错误的!!!!!!! 请参阅 https://www.connectionstrings.com/sql-azure/ 进行确认。
在我的例子中,发生这个错误是因为我在帐户控制器的登录方法中添加了另一个 SignInManager(以添加角色为例)。它在同一方法中执行 SignInManager.PasswordSignInAsync 和 SignInManager.SignInAsync 并且这两次调用 ApplicationUser 中的 GenerateUserIdentityAsync(...) ,第一次成功,第二次给了我异常 "UserId"未找到。