.NET 8.0中Session与授权协同工作的配置问题
问题根源
Session和ASP.NET Identity的授权Cookie是两个完全独立的机制:
- Session由
AddSession配置,依赖Session Cookie和分布式缓存,超时由IdleTimeout控制 - 授权Cookie由
ConfigureApplicationCookie配置,超时由ExpireTimeSpan和SlidingExpiration控制
当你把ExpireTimeSpan调大(比如14天)、IdleTimeout保持20分钟时,Session会先过期,但授权Cookie仍有效,导致用户被判定为已登录,但访问Session时因Session不存在而崩溃。
关键修复:调整中间件顺序
你的Program.cs中中间件顺序错误,UseSession必须在UseAuthorization之前执行,否则授权逻辑无法访问Session,且Session初始化不及时。修正后的顺序:
app.UseRouting(); // 先启用Session app.UseSession(); // 再执行授权 app.UseAuthorization(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto });
解决方案一:让Session与授权Cookie同步过期
要实现「闲置20分钟自动登出,Cookie默认有效期14天」的需求,需调整两者的配置逻辑,让Session过期时同步失效授权Cookie:
1. 配置基础参数
// Session配置:闲置20分钟过期 builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(20); options.Cookie.Name = ".PBP.Session"; options.Cookie.IsEssential = true; // 保持与授权Cookie同域同路径,避免跨域问题 options.Cookie.Domain = builder.Configuration["CookieDomain"]; options.Cookie.Path = "/"; }); // 授权Cookie配置:默认14天有效期,滑动过期(用户活动则延长有效期) builder.Services.ConfigureApplicationCookie(options => { options.ExpireTimeSpan = TimeSpan.FromDays(14); options.LoginPath = "/Identity/Account/Login"; options.SlidingExpiration = true; options.Cookie.Domain = builder.Configuration["CookieDomain"]; options.Cookie.Path = "/"; });
2. 添加自定义中间件同步过期
在UseSession之后、UseAuthorization之前添加中间件,检查Session有效性,若Session过期则登出用户:
app.UseSession(); // 新增:Session过期时同步登出用户 app.Use(async (context, next) => { if (context.User.Identity?.IsAuthenticated == true) { try { // 尝试读取业务中必须存在的Session键(示例为UserId),触发Session过期检查 var hasValidSession = context.Session.TryGetValue("UserId", out _); if (!hasValidSession) { await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); context.Response.Redirect("/Identity/Account/Login"); return; } } catch (InvalidOperationException) { // Session已从缓存中清除,执行登出 await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); context.Response.Redirect("/Identity/Account/Login"); return; } } await next(); }); app.UseAuthorization();
解决方案二:使用ActionFilter全局检查
如果不想用中间件,可创建全局ActionFilter,在所有带[Authorize]特性的Action执行前检查Session:
1. 定义Filter
public class SessionCheckFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var httpContext = context.HttpContext; if (httpContext.User.Identity?.IsAuthenticated == true) { try { var hasValidSession = httpContext.Session.TryGetValue("UserId", out _); if (!hasValidSession) { await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); httpContext.Response.Redirect("/Identity/Account/Login"); return; } } catch (InvalidOperationException) { await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); httpContext.Response.Redirect("/Identity/Account/Login"); return; } } await next(); } }
2. 注册全局Filter
在Program.cs中添加:
builder.Services.AddControllersWithViews(options => { options.Filters.Add<SessionCheckFilter>(); });
额外注意事项
- 若使用分布式缓存(如Redis),需确保Session的缓存配置正确,避免Session提前被清除
- 不要依赖Session的
IsAvailable属性判断过期,需通过尝试读取具体Session键或捕获异常来判断 - 滑动过期模式下,用户每次活动都会同时延长Session和授权Cookie的有效期
内容的提问来源于stack exchange,提问作者user517406
相关产品推荐
相关产品推荐

