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

ASP.NET Core谷歌登录异常:AspNetUserLogins表未填充,登录失败

解决Google登录AspNetUserLogins未填充及ExternalLoginSignInAsync失败问题

你的核心问题是:当前代码仅处理了外部登录已关联本地用户的成功/锁定场景,但当外部登录未与任何本地用户关联时,没有执行创建新用户并绑定外部登录或将外部登录与已有本地用户关联的逻辑,这直接导致AspNetUserLogins表无数据,且ExternalLoginSignInAsync返回失败。

以下是具体修复步骤:

1. 完善ExternalLoginCallback的未关联处理逻辑

替换你当前的else分支,添加用户创建/关联逻辑:

public async Task<IActionResult> ExternalLoginCallback(string? returnUrl = null) 
{
    var info = await _signInManager.GetExternalLoginInfoAsync(); 

    if (info == null) { 
        return RedirectToAction(nameof(Login)); 
    }

    var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);

    if (signInResult.Succeeded)
    {
        return RedirectToLocal(returnUrl);
    }
    if (signInResult.IsLockedOut)
    {
        return RedirectToAction(nameof(ForgotPassword));
    }
    else
    {
        ViewData["ReturnUrl"] = returnUrl;
        ViewData["Provider"] = info.LoginProvider;
        var email = info.Principal.FindFirstValue(ClaimTypes.Email);
        
        // 检查是否已有该邮箱的本地用户
        var user = await _userManager.FindByEmailAsync(email);
        if (user != null)
        {
            // 将外部登录与已有用户关联
            var result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            }
            // 处理关联失败的错误
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }
        
        return View("ExternalLogin", new ExternalLoginModel { Email = email });
    }
}

2. 实现ExternalLoginConfirmation处理用户提交

当用户在ExternalLogin视图提交邮箱后,需要对应的Action完成新用户创建与关联:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginModel model, string? returnUrl = null)
{
    if (ModelState.IsValid)
    {
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null)
        {
            return View("Error");
        }
        
        // 创建新用户
        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
        var createResult = await _userManager.CreateAsync(user);
        if (createResult.Succeeded)
        {
            // 关联外部登录记录(这一步会写入AspNetUserLogins表)
            var loginResult = await _userManager.AddLoginAsync(user, info);
            if (loginResult.Succeeded)
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            }
            foreach (var error in loginResult.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }
        foreach (var error in createResult.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    ViewData["ReturnUrl"] = returnUrl;
    return View(model);
}

3. 验证Google OAuth配置正确性

确保在Program.cs/Startup.cs中正确配置Google登录,请求必要的权限:

builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
        // 确保请求email和profile权限,以便获取用户信息
        options.Scope.Add("email");
        options.Scope.Add("profile");
    });

关键说明:AspNetUserLogins表的记录是在调用AddLoginAsync时写入的,只有完成这一步,后续的ExternalLoginSignInAsync才能成功匹配并登录用户。

内容的提问来源于stack exchange,提问作者milko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:15:59