.NET Core MVC中UserManager.GetUserAsync返回null问题排查
问题分析:外部登录后无法获取用户ID及用户对象
问题场景
我有两个项目:项目1为VB项目,项目2为.NET Core MVC项目。项目1向项目2传输数据,项目2根据接收的数据在数据库中创建新的Identity用户并完成登录,登录流程正常执行,但重定向到HomeController的Index方法后,_userManager.GetUserId(User)和_userManager.GetUserAsync(User)返回的id和user均为null。
相关代码
CreateExternalUser方法:
public async Task<IActionResult> CreateExternalUser(string externalJson) { var externalTokenDetails = JsonConvert.DeserializeObject<ExternalTokenDetails>(externalJson); var company = _admin.GetCompanyByClientCode(externalTokenDetails.ClientCode); if (company != null) { var existingUser = await _userManager.FindByEmailAsync(externalTokenDetails.Email); if (existingUser != null) { var claims = new List<Claim> { new Claim(ClaimTypes.Name, existingUser.UserName), new Claim(ClaimTypes.Email, existingUser.Email), new Claim("FullName", $"{externalTokenDetails.Name} {externalTokenDetails.Surname}"), new Claim("ClientCode", externalTokenDetails.ClientCode), new Claim("ClientName", externalTokenDetails.ClientName), new Claim(ClaimTypes.Role, externalTokenDetails.RoleName), }; var userIdentity = new ClaimsIdentity(claims, IdentityConstants.ApplicationScheme); var userPrincipal = new ClaimsPrincipal(userIdentity); await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, userPrincipal); return RedirectToAction("Index", "Home"); } else { var user = new ApplicationUser { UserName = externalTokenDetails.Email, Email = externalTokenDetails.Email, CompanyId = company.Id, Company = company, }; var result = await _userManager.CreateAsync(user); // Persist the user if (result.Succeeded) { // User created successfully, sign them in var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Email, user.Email), new Claim("FullName", $"{externalTokenDetails.Name} {externalTokenDetails.Surname}"), new Claim("ClientCode", externalTokenDetails.ClientCode), new Claim("ClientName", externalTokenDetails.ClientName), new Claim(ClaimTypes.Role, externalTokenDetails.RoleName), }; var userIdentity = new ClaimsIdentity(claims, IdentityConstants.ApplicationScheme); var userPrincipal = new ClaimsPrincipal(userIdentity); await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, userPrincipal); return RedirectToAction("Index", "Home"); } else { return RedirectToAction("Index", "Home"); } } } else { return RedirectToAction("Index", "Home"); } }
HomeController的Index方法:
System.Security.Claims.ClaimsPrincipal currentUser = this.User; bool isAdmin = currentUser.IsInRole("Admin"); var id = _userManager.GetUserId(User); // Get user id: var user = await _userManager.GetUserAsync(User);
问题原因
UserManager.GetUserId(User)和UserManager.GetUserAsync(User)的核心逻辑是从当前ClaimsPrincipal中读取ClaimTypes.NameIdentifier类型的Claim值,以此作为用户ID去数据库查找用户。但你在登录时构造的Claims列表中,没有添加这个关键Claim,导致这两个方法无法获取到用户ID,最终返回null。
解决方案
方案1:添加NameIdentifier Claim到登录凭证中
在构造Claims列表时,加入用户ID对应的NameIdentifier Claim:
- 对于已有用户(existingUser):
var claims = new List<Claim> { new Claim(ClaimTypes.Name, existingUser.UserName), new Claim(ClaimTypes.Email, existingUser.Email), new Claim(ClaimTypes.NameIdentifier, existingUser.Id), // 新增这一行 new Claim("FullName", $"{externalTokenDetails.Name} {externalTokenDetails.Surname}"), new Claim("ClientCode", externalTokenDetails.ClientCode), new Claim("ClientName", externalTokenDetails.ClientName), new Claim(ClaimTypes.Role, externalTokenDetails.RoleName), };
- 对于新创建的用户(user):
var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.NameIdentifier, user.Id), // 新增这一行 new Claim("FullName", $"{externalTokenDetails.Name} {externalTokenDetails.Surname}"), new Claim("ClientCode", externalTokenDetails.ClientCode), new Claim("ClientName", externalTokenDetails.ClientName), new Claim(ClaimTypes.Role, externalTokenDetails.RoleName), };
添加后,GetUserId和GetUserAsync就能正常获取到用户ID和对象。
方案2:通过Email直接查找用户
如果不想修改Claims列表,可以利用已有的Email Claim来查询用户:
var email = User.FindFirstValue(ClaimTypes.Email); var user = await _userManager.FindByEmailAsync(email); var id = user?.Id;
这种方式跳过了依赖NameIdentifier Claim的逻辑,直接通过用户邮箱从数据库获取用户对象,进而得到用户ID。
方案3:使用UserManager的GetUserAsync重载(通过用户名)
也可以通过用户名查找用户:
var userName = User.FindFirstValue(ClaimTypes.Name); var user = await _userManager.FindByNameAsync(userName); var id = user?.Id;
内容的提问来源于stack exchange,提问作者MarkBerry
相关产品推荐
相关产品推荐

