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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:25:17