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

ASP.NET Core 6 Identity无法验证/登录用户,原因何在?

ASP.NET Core Identity登录失败问题排查与解决

问题背景

技术栈:

  • ASP.NET Core 6 MVC
  • Entity Framework Core
  • ASP.NET Core Identity

预填充用户数据实现

用户类定义:

public class User: IdentityUser<long>

预填充前的数据处理逻辑:

PasswordHasher<User> ph = new PasswordHasher<User>();
Users.ForEach(c => c.PasswordHash = ph.HashPassword(c, c.PasswordHash));
Users.ForEach(c => c.NormalizedEmail = c.Email.ToUpper());
Users.ForEach(c=>c.NormalizedUserName=c.UserName.ToUpper());
Users.ForEach(c => c.SecurityStamp = Guid.NewGuid().ToString("D"));
Users.ForEach(c => c.ConcurrencyStamp = Guid.NewGuid().ToString("D"));

EF Core数据种子代码:

modelBuilder.Entity<User>().HasData(PatientMockDB.Users);

Program.cs关键配置

builder.Services.AddIdentity<User, IdentityRole<long>>(options =>
{
    options.SignIn.RequireConfirmedAccount = false;
    options.SignIn.RequireConfirmedPhoneNumber = false;
    options.SignIn.RequireConfirmedEmail = false;

})
    .AddRoles<IdentityRole<long>>()
    .AddEntityFrameworkStores<ADbContext>();

builder.Services.ConfigureApplicationCookie(options =>
{
    options.AccessDeniedPath = "/HomeArea/Home/Denied";
    options.Cookie.Name = "mycookie";
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
    options.LoginPath = "/HomeArea/Home/Login";
    options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
    options.SlidingExpiration = true;
});

// 额外添加的Cookie认证配置
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
    options.Cookie.IsEssential = true;
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.None;
});

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

// 中间件顺序
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();

登录方法代码

[HttpPost]
public async Task<IActionResult> LogInEmployee(LogInViewModel model)
{
    if (model==null)
    {
        return View("LogIn", model);
    }
    long userId = model.User.Id;
    model.User.UserType = Core.Enums.UserType.Employee;
    User user = _context.CheckEmailAndRole(model.User);

    if (user != null)
    {
        TempData["User"] = JsonConvert.SerializeObject(user);
        user = await _userManager.FindByEmailAsync(user.Email);

        var result = await _signManager.PasswordSignInAsync(user.UserName, model.User.PasswordHash,false,false);
        result = await _signManager.PasswordSignInAsync(user.Email, model.User.PasswordHash, false, false);
        result = await _signManager.PasswordSignInAsync(user.UserName, user.PasswordHash, false, false);
        result = await _signManager.PasswordSignInAsync(user.Email, user.PasswordHash, false, false);
        result = await _signManager.PasswordSignInAsync(user, user.PasswordHash, false, false);
        result = await _signManager.PasswordSignInAsync(user, model.User.PasswordHash, false, false);
        SignInResult signInResult = await _signManager.CheckPasswordSignInAsync(user, model.User.PasswordHash, false);
        signInResult = await _signManager.CheckPasswordSignInAsync(user, user.PasswordHash, false);
        bool lol=await _signManager.CanSignInAsync(user);
        await _signManager.SignInAsync(user, false);

        if (result.Succeeded)
        {
       // 业务逻辑
            }
        }
    }               
    else
    {
        
    }
    model.LogInFailed = true;
    return View("LogIn", model);
}

数据库上下文定义:

public class ADbContext : IdentityDbContext<User,IdentityRole<long>,long>, I1Repository, I2Repository, I3Repository, I4Repository, I5Repository

核心问题:尝试多种登录方式均失败,需排查并解决。


问题排查与解决方案

1. 重复Cookie认证配置导致冲突

AddIdentity已经默认配置了Identity专属的Cookie认证,额外调用AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(...)会覆盖或干扰默认的认证流程,导致登录凭证无法正确生成或验证。

修复:
删除额外的AddAuthentication配置块,将Cookie的安全策略配置合并到ConfigureApplicationCookie中:

builder.Services.ConfigureApplicationCookie(options =>
{
    options.AccessDeniedPath = "/HomeArea/Home/Denied";
    options.Cookie.Name = "mycookie";
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.None;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
    options.LoginPath = "/HomeArea/Home/Login";
    options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
    options.SlidingExpiration = true;
});

2. 预填充数据的潜在问题

(1)密码哈希逻辑验证

确认预填充时,c.PasswordHash是明文密码,而不是已哈希的值。如果预填充前该字段已经是哈希值,二次哈希会导致登录时密码验证失败。

(2)SecurityStamp验证

Identity登录时会校验SecurityStamp,若预填充的Stamp与运行时逻辑不匹配,可能导致登录失败。可尝试在登录前更新Stamp:

await _userManager.UpdateSecurityStampAsync(user);

3. 登录方法的逻辑错误

(1)覆盖result变量导致无法排查

多次调用PasswordSignInAsync后,只有最后一次的结果被保留,无法知道哪次尝试成功。需单独检查每个调用的返回状态,或只保留正确的调用方式。

(2)错误的密码参数传递

PasswordSignInAsync和CheckPasswordSignInAsync的第二个参数需要明文密码,而非数据库中存储的哈希值。你传入user.PasswordHash的调用必然失败,正确调用方式:

// 用户名+明文密码
var result = await _signManager.PasswordSignInAsync(user.UserName, model.User.PasswordHash, false, false);
// 或邮箱+明文密码
var result = await _signManager.PasswordSignInAsync(user.Email, model.User.PasswordHash, false, false);
// 或先验证密码再登录
var signInResult = await _signManager.CheckPasswordSignInAsync(user, model.User.PasswordHash, false);
if (signInResult.Succeeded)
{
    await _signManager.SignInAsync(user, false);
}

(3)直接调用SignInAsync无效

若密码验证未通过,直接调用SignInAsync无法生成有效的认证票据,必须在密码验证成功后再执行登录操作。

4. 中间件顺序错误

UseSession的位置不符合ASP.NET Core推荐顺序,会影响会话数据读取,进而干扰认证流程。

修复:
调整中间件顺序为:

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession(); // 移到授权之后

5. 数据库数据验证

直接查询数据库,确认以下字段:

  • PasswordHash:是有效的哈希值,而非明文或错误哈希
  • NormalizedEmail/NormalizedUserName:与用户输入的邮箱/用户名转大写后一致
  • EmailConfirmed/PhoneNumberConfirmed:均为true(即使配置关闭验证,数据库值需匹配)

修复后的登录方法示例

[HttpPost]
public async Task<IActionResult> LogInEmployee(LogInViewModel model)
{
    if (model == null || string.IsNullOrWhiteSpace(model.User.PasswordHash))
    {
        model.LogInFailed = true;
        return View("LogIn", model);
    }

    model.User.UserType = Core.Enums.UserType.Employee;
    var userFromDb = _context.CheckEmailAndRole(model.User);
    if (userFromDb == null)
    {
        model.LogInFailed = true;
        return View("LogIn", model);
    }

    var user = await _userManager.FindByEmailAsync(userFromDb.Email);
    if (user == null)
    {
        model.LogInFailed = true;
        return View("LogIn", model);
    }

    var signInResult = await _signManager.CheckPasswordSignInAsync(user, model.User.PasswordHash, lockoutOnFailure: false);
    if (signInResult.Succeeded)
    {
        await _signManager.SignInAsync(user, isPersistent: false);
        return RedirectToAction("Index", "Home", new { area = "HomeArea" });
    }
    else
    {
        model.LogInFailed = true;
        // 可根据signInResult状态返回具体错误(如锁定、未授权等)
        return View("LogIn", model);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:25:51