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

如何修改Razor页面AutoLoginMiddleware实现会话超时重定向至SessionExpired页

内网Razor页面中区分认证Cookie与Session超时并自动重定向的解决方案

核心判断逻辑

要区分两种超时,关键是利用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();

关键逻辑说明

  1. Session超时判断:用户已通过Identity认证,但Session中无活跃标记 → 说明Session因IdleTimeout过期
  2. 认证Cookie超时判断:用户未通过Identity认证,但Windows身份有效且Session有活跃标记 → 说明之前登录过,现在Identity的认证Cookie已过期
  3. 双超时/首次访问:用户未认证且Session无标记 → 视为首次访问,执行自动登录流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:54:54