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

.NET Core应用重启后用户保持登录的原因及解决方法

问题原因

ASP.NET Core 基于Cookie的认证机制核心逻辑是:用户登录成功后,服务端会将身份声明(Claims)加密后写入客户端浏览器的Cookie中,后续请求只要携带未过期、可被服务端正常解密的认证Cookie,就会直接判定用户为已登录状态,不需要依赖服务端存储的会话数据。
应用重启后登录状态保留的核心原因有两个:

  • 认证Cookie被设置为持久化模式:登录逻辑中设置了IsPersistent = true,Cookie会被持久化存储到用户本地磁盘,不会随浏览器关闭、应用重启自动删除,且配置的Cookie有效期为60分钟,有效期内浏览器每次请求都会自动携带该Cookie。
  • 服务端未配置重启后的凭证失效校验:默认配置下,ASP.NET Core的数据保护密钥会持久化存储在操作系统/容器的固定目录下(IIS Express环境存在当前用户的APPDATA目录,Docker环境如果是重启未销毁容器,密钥会保留在容器文件系统中),应用重启后依然可以用原有密钥解密客户端传来的旧认证Cookie,没有额外的校验逻辑拦截旧实例签发的凭证。如果是销毁容器后重新创建实例,默认配置下数据保护密钥会重置,旧Cookie自然解密失败,但如果是重启未销毁容器、或者显式配置了持久化密钥的场景,旧Cookie依然有效。
导致问题的代码段

一共有两处相关配置导致该现象:

  1. Startup.cs中Cookie认证的配置段,未添加应用实例标识校验、未配置随应用重置的加密密钥:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Home/Index";
        options.AccessDeniedPath = "/Error/403";
        // 此处缺少重启失效相关配置
    });
  1. HomeController.cs登录逻辑中,显式开启了持久化Cookie:
AuthenticationProperties authProperties = new AuthenticationProperties
{
    AllowRefresh = true,
    ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60),
    IsPersistent = true, // 该行设置Cookie持久化存储到本地
    IssuedUtc = DateTimeOffset.UtcNow
};

另外当前代码的中间件顺序存在错误,app.UseSession()需要放在app.UseAuthentication()和app.UseAuthorization()之前,否则认证流程无法正常读取Session数据,虽然不是本次问题的诱因,但需要修正。

修复方案

要实现应用重启/重新部署后所有已登录用户凭证自动失效,按以下步骤修改即可:

  1. 在Startup类中添加一个静态变量,存储当前应用实例的唯一启动标识,应用每次重启该值都会重新生成:
public class Startup
{
    // 应用启动时自动生成唯一ID,重启后重置
    private static readonly string _appInstanceStamp = Guid.NewGuid().ToString("N");

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    // 其余原有代码保持不变
}
  1. 修改ConfigureServices中的Cookie认证配置,添加票据签发和校验逻辑:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Home/Index";
        options.AccessDeniedPath = "/Error/403";
        options.Events = new CookieAuthenticationEvents
        {
            // 用户登录签发Cookie时,把当前实例标识写入票据
            OnSigningIn = context =>
            {
                context.Properties.Items["AppInstanceStamp"] = _appInstanceStamp;
                return Task.CompletedTask;
            },
            // 每次请求校验Cookie时,对比实例标识
            OnValidatePrincipal = async context =>
            {
                // 票据中没有标识、或者标识和当前实例不匹配,判定凭证失效
                if (!context.Properties.Items.TryGetValue("AppInstanceStamp", out var stamp) 
                    || stamp != _appInstanceStamp)
                {
                    context.RejectPrincipal();
                    await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                }
            }
        };
    });
  1. (可选)如果不需要浏览器关闭后依然保持登录状态,可以把登录逻辑中的IsPersistent = true改为IsPersistent = false,此时认证Cookie为会话级Cookie,浏览器关闭后就会自动删除。
  2. 修正中间件顺序,把UseSession移到UseAuthentication之前:
app.UseRouting();

// 调整Session中间件位置
app.UseSession();

app.UseAuthentication();
app.UseAuthorization();

// 其余原有中间件保持不变

修改完成后,每次应用重启/重新部署,_appInstanceStamp都会生成新的随机值,所有旧实例签发的认证Cookie都会因为标识不匹配被直接拒绝,强制用户跳转到登录页重新登录,该方案不依赖数据保护密钥的存储逻辑,可以覆盖所有重启/重部署场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:09:17