ASP.NET Core中Security Stamp验证失效,无法踢除重复登录用户
问题分析与修复方案:ASP.NET Core Identity 同账号登录踢除旧会话失效问题
核心问题排查
你的代码存在以下关键问题导致功能失效:
1. Login控制器逻辑错误且缺失
- 仅当
user.Session==false时才更新SecurityStamp,旧登录状态的用户再次登录时不会触发SecurityStamp更新,旧会话无法失效 - 未执行实际登录流程,缺少SignInManager的登录调用,仅修改了用户字段
- 直接调用
_context.SaveChanges()存在冗余,UpdateSecurityStampAsync已自动保存用户数据
2. Cookie认证未关联SecurityStamp验证
仅配置了SecurityStampValidatorOptions.ValidationInterval=TimeSpan.Zero,但未在Cookie认证配置中启用SecurityStamp的Principal验证逻辑,导致旧会话不会被校验失效
3. Identity服务注册不完整
使用AddIdentityCore时,未确保SecurityStampValidator服务正确注册,SecurityStamp的验证逻辑无法运行
修复代码
修正Login控制器
[HttpPost] public async Task<IActionResult> Login(LoginDto loginDto) { if(!ModelState.IsValid) return View(loginDto); var user = await _userManager.FindByNameAsync(loginDto.Username); if(user == null) { ModelState.AddModelError(string.Empty, "无效的登录凭据"); return View(loginDto); } // 无论用户之前是否登录,新登录时更新SecurityStamp,使旧会话失效 await _userManager.UpdateSecurityStampAsync(user); user.LoginDate = DateTime.Now; await _userManager.UpdateAsync(user); // 用UserManager的方法统一保存 // 执行实际登录操作 var result = await _signInManager.PasswordSignInAsync(user, loginDto.Password, loginDto.RememberMe, lockoutOnFailure: false); if(result.Succeeded) { return RedirectToAction("Index", "Home"); } ModelState.AddModelError(string.Empty, "登录失败,请检查用户名或密码"); return View(loginDto); }
修正Identity服务扩展
public static class IdentityServiceExtensions { public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config) { services.AddIdentityCore<AppUser>() .AddRoles<Role>() .AddEntityFrameworkStores<DataContext>() .AddSignInManager<CustomSignInManager<AppUser>>() .AddSecurityStampValidator<SecurityStampValidator<AppUser>>(); // 确保注册SecurityStamp验证服务 services.Configure<SecurityStampValidatorOptions>(options => { options.ValidationInterval = TimeSpan.Zero; // 每次请求都验证SecurityStamp }); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(opt => { opt.Cookie.Name = "cClient"; opt.Cookie.HttpOnly = true; opt.LoginPath = "/Auth/Login/"; opt.LogoutPath = "/Auth/Logout/"; opt.ExpireTimeSpan = TimeSpan.FromMinutes(10); opt.AccessDeniedPath = "/Error/Handle/401"; // 关键:启用SecurityStamp验证,自动失效旧会话 opt.Events = new CookieAuthenticationEvents { OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync }; }); services.Configure<CookiePolicyOptions>(opt => { opt.MinimumSameSitePolicy = SameSiteMode.Strict; opt.ConsentCookie.HttpOnly = true; }); services.AddMvc(opt => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); opt.Filters.Add(new AuthorizeFilter(policy)); }); services.AddAuthorization(opt => { opt.AddPolicy("HoOnly", policy => policy.Requirements.Add(new HoOnly())); }); return services; } }
Startup代码无需修改
当前Startup的中间件顺序正确(UseAuthentication在UseAuthorization之前),无需调整
原理说明
当用户再次登录时,UpdateSecurityStampAsync会更新用户的SecurityStamp值,由于配置了ValidationInterval=TimeSpan.Zero,旧会话的Cookie在下次请求时会被验证,发现SecurityStamp不匹配,会自动触发登录失效,跳转到登录页。
内容的提问来源于stack exchange,提问作者Jimy
相关产品推荐
相关产品推荐

