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

ASP.NET Core中Cookie/Session提前过期问题求助

问题现象

在ASP.NET Core项目的Program.cs中已将Cookie和Session的过期时间设置为7天,但系统仍会在30分钟后自动登出。Chrome中查看Cookie过期时间显示为2025年(符合预期),多次调整配置代码无效,怀疑自定义CookieMiddleware是诱因。

项目配置代码

builder.Services.AddAuthentication(option =>
{
    option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;    
}).AddCookie(option =>
{
    option.LoginPath = new PathString("/home/login");
    option.ExpireTimeSpan = TimeSpan.FromDays(7);
    option.AccessDeniedPath = new PathString("/");

    option.SlidingExpiration = false;  
    option.Cookie.HttpOnly = true;  
    option.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
    option.Cookie.Name = "UserAuthCookie";

}).AddGoogle("GOOGLE_LOGIN", option =>
{
    option.ClientId = "0000000000000-0000000000000.apps.googleusercontent.com";
    option.ClientSecret = "0000000000-0000000000000";
    option.CallbackPath = "/home/GoogleLoginCallback/";
    option.Events = new OAuthEvents
    {
        OnRemoteFailure = context =>
        {
            context.Response.Redirect("/Home/login");
            context.HandleResponse();
            return Task.CompletedTask;
        }
    };
}).AddGoogle("GOOGLE_SIGNUP", option =>
{
    option.ClientId = "00000000-000000000.apps.googleusercontent.com";
    option.ClientSecret = "00000000000-0000000000";
    option.CallbackPath = "/home/GoogleSignupCallback/";
    option.Events = new OAuthEvents
    {
        OnRemoteFailure = context =>
        {
            context.Response.Redirect("/Home/signup");
            context.HandleResponse();
            return Task.CompletedTask;
        }
    };
});

builder.Services.AddCors(options =>
{
    options.AddPolicy("CorsPolicy",
        builder => builder.WithOrigins("https://filmnamenevisan.ir") 
                          .AllowAnyMethod()
                          .AllowAnyHeader()
                          .AllowCredentials()); // allow cookies
});
builder.Services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Name = "UserAuthCookie";
    options.Cookie.SameSite = SameSiteMode.None;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always; 
    options.Cookie.HttpOnly = true; 
    options.ExpireTimeSpan = TimeSpan.FromDays(7);
    options.SlidingExpiration = false; 
});

builder.Services.AddAuthorization(option =>
{
    option.AddPolicy(RolesConstants.King, policy => policy.RequireRole(RolesConstants.King));
    option.AddPolicy(RolesConstants.Admin, policy => policy.RequireRole(RolesConstants.Admin));
    option.AddPolicy(RolesConstants.Expert, policy => policy.RequireRole(RolesConstants.Expert));
    option.AddPolicy(RolesConstants.User, policy => policy.RequireRole(RolesConstants.User));
});

builder.Services.AddSession(options =>
{
    options.Cookie.Name = "UserSession";
    options.IdleTimeout = TimeSpan.FromDays(7); 
    options.Cookie.HttpOnly = true;   
    options.Cookie.SameSite = SameSiteMode.None; 
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");    
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseCookiePolicy();

app.UseSession();

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

app.UseMiddleware<CookieMiddleware>();

自定义CookieMiddleware代码

public class CookieMiddleware
{
     private readonly RequestDelegate _next;
     private readonly IServiceProvider _serviceProvider;

     public CookieMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
     {
         _next = next;
         _serviceProvider = serviceProvider;
     }

     public async Task Invoke(HttpContext context)
     {
         if (context.User.Identity.IsAuthenticated)
         {
             if (!context.Session.Keys.Contains("UserSession") || !context.Session.TryGetValue("UserSession:user-id", out _))
             {
                 context.Session.SetString("UserSession:user-id", ClaimUtility.GetUserId(context.User as ClaimsPrincipal).ToString());
                 context.Session.SetString("UserSession:user-nickname", ClaimUtility.GetUserFullname(context.User as ClaimsPrincipal));

                 using (var scope = _serviceProvider.CreateScope())
                 {
                     var dbContext = scope.ServiceProvider.GetRequiredService<IUserFacade>();

                     var retrieve = dbContext.GetUserUsernameHeadshotByIdService.Execute(new RequestGetUserUsernameHeadshotByIdServiceDto
                     {
                         UserId = Guid.Parse(context.Session.GetString("UserSession:user-id")),
                     });

                     if (retrieve != null)
                     {
                         if (retrieve.Username != null) context.Session.SetString("UserSession:user-username", retrieve.Username);
                         if (string.IsNullOrEmpty(retrieve.Headshot))
                             context.Session.SetString("UserSession:user-privateheadshot", GeneralConstants.PublicHeadshot);
                         else
                             context.Session.SetString("UserSession:user-privateheadshot", $"/UploadedStuff/user/images/user-headshot/{retrieve.Headshot}-thumb.jpg");
                     }
                     else
                     {
                         context.Session.SetString("UserSession:user-privateheadshot", GeneralConstants.PublicHeadshot);
                     }
                 }
             }
         }

         await _next(context);
     }
 }

排查解决方法

1. 修正中间件执行顺序

当前自定义中间件CookieMiddleware注册在UseAuthorization()之后,此时用户身份信息可能还未完成认证初始化,导致Session逻辑异常。调整顺序,将其移到UseAuthentication()之后、UseAuthorization()之前:

app.UseAuthentication();
app.UseMiddleware<CookieMiddleware>(); // 调整到此处
app.UseRouting();
app.UseAuthorization();

ASP.NET Core中间件按注册顺序执行,认证完成后才能正确获取context.User的身份信息,确保自定义中间件能正确处理Session数据。

2. 修复Session键判断逻辑

中间件中!context.Session.Keys.Contains("UserSession")的判断永远为true,因为Session的Keys存储的是具体键名(如UserSession:user-id),不存在UserSession这个键。修改判断逻辑:

// 原代码
// if (!context.Session.Keys.Contains("UserSession") || !context.Session.TryGetValue("UserSession:user-id", out _))
// 修改为
if (!context.Session.TryGetValue("UserSession:user-id", out _))

避免每次请求都重复初始化Session,减少不必要的Session操作。

3. 确认登录时的Ticket过期时间

检查登录逻辑,确保调用SignInAsync时指定持久化属性和过期时间,覆盖默认30分钟设置:

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(identity),
    new AuthenticationProperties
    {
        ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7),
        IsPersistent = true
    });

即使在AddCookie中配置了ExpireTimeSpan,部分场景下(如第三方登录回调)仍需显式指定过期属性。

4. 统一Cookie安全策略配置

AddCookie和ConfigureApplicationCookie中CookieSecurePolicy配置不一致,统一设置为CookieSecurePolicy.Always,确保HTTPS环境下Cookie正常传递:

// 在AddCookie中修改
option.Cookie.SecurePolicy = CookieSecurePolicy.Always;

避免配置冲突导致浏览器拒绝Cookie。

5. 检查Session存储方式(生产环境)

默认Session使用内存存储,应用池回收(默认30分钟)会导致Session丢失。生产环境建议使用分布式缓存(如Redis)持久化Session:

// 示例:添加Redis缓存
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "your-redis-connection-string";
    options.InstanceName = "SampleInstance";
});

确保Session数据不会因应用池回收丢失。

内容的提问来源于stack exchange,提问作者Ali.M Eghbaldar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:42:01