You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 03:19:50