如何修改Razor页面AutoLoginMiddleware实现会话超时重定向至SessionExpired页
核心判断逻辑
要区分两种超时,关键是利用Session持久标记和ASP.NET Identity登录状态做交叉验证:
- 登录成功时在Session中存储活跃标记,用于追踪Session有效性
- 通过「登录状态」和「Session标记存在性」的组合,精准判断超时类型
步骤1:修改AutoLoginMiddleware
在中间件中加入超时判断逻辑,同时避免循环重定向:
public class AutoLoginMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; private const string SessionActiveKey = "SessionActive"; private const string SessionExpiredPath = "/Security/SessionExpired"; public AutoLoginMiddleware(RequestDelegate next, ILogger<AutoLoginMiddleware> logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context, UserService userService, UserManager<IntranetUser> userManager, SignInManager<IntranetUser> signInManager) { // 排除SessionExpired页面,防止循环重定向 if (context.Request.Path.StartsWithSegments(SessionExpiredPath)) { await _next(context); return; } if (signInManager.IsSignedIn(context.User)) { // 情况1:已登录但Session超时 if (string.IsNullOrEmpty(context.Session.GetString(SessionActiveKey))) { _logger.LogInformation($"Session expired for user {context.User.Identity.Name}"); context.Response.Redirect(SessionExpiredPath); return; } // 更新Session活跃标记,维持滑动超时 context.Session.SetString(SessionActiveKey, DateTime.UtcNow.ToString("o")); _logger.LogInformation("User already signed in, session active"); } else { if (context.User.Identity is WindowsIdentity windowsIdentity && windowsIdentity.IsAuthenticated) { var windowsLogin = windowsIdentity.Name; var hasActiveSession = !string.IsNullOrEmpty(context.Session.GetString(SessionActiveKey)); // 情况2:未登录但Session仍有效 → 认证Cookie超时 if (hasActiveSession) { _logger.LogInformation($"Authentication cookie expired for Windows user {windowsLogin}"); context.Response.Redirect(SessionExpiredPath); return; } // 情况3:首次访问或双超时 → 执行自动登录 else { _logger.LogInformation($"Windows user {windowsLogin} requires auto-login"); var user = await userManager.Users.FirstOrDefaultAsync(u => u.NormalizedWindowsLogin == windowsLogin.ToUpperInvariant()); if (user != null) { await signInManager.SignInAsync(user, true, "automatic"); // 登录成功后写入Session活跃标记 context.Session.SetString(SessionActiveKey, DateTime.UtcNow.ToString("o")); _logger.LogInformation($"User {user.UserName} (ID: {user.Id}) auto-logged in successfully"); context.Items["IntranetUser"] = user; } else { _logger.LogError("User not found in identity store"); throw new InvalidOperationException("user not found."); } } } else { // 非Windows认证用户,直接重定向到超时页 _logger.LogInformation("No valid Windows identity detected"); context.Response.Redirect(SessionExpiredPath); return; } } await _next(context); } }
步骤2:确认中间件注册顺序
确保Session和认证中间件的顺序正确,否则无法正常读取Session和用户身份:
var builder = WebApplication.CreateBuilder(args); // 注册服务... builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); builder.Services.ConfigureApplicationCookie(opt => { opt.ExpireTimeSpan = TimeSpan.FromMinutes(30); opt.SlidingExpiration = true; opt.LoginPath = "/Security/SessionExpired"; }); var app = builder.Build(); // 中间件顺序:路由 → Session → 认证 → 授权 → 自定义AutoLogin app.UseRouting(); app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.UseMiddleware<AutoLoginMiddleware>(); // 其他中间件... app.MapRazorPages(); app.Run();
关键逻辑说明
- Session超时判断:用户已通过Identity认证,但Session中无活跃标记 → 说明Session因IdleTimeout过期
- 认证Cookie超时判断:用户未通过Identity认证,但Windows身份有效且Session有活跃标记 → 说明之前登录过,现在Identity的认证Cookie已过期
- 双超时/首次访问:用户未认证且Session无标记 → 视为首次访问,执行自动登录流程
内容的提问来源于stack exchange,提问作者zbx888
相关产品推荐
相关产品推荐

