ASP.NET Core 6 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

