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

强制Blazor Web App每次启动时重新登录(不保留会话登录信息)

解决Blazor Web App重启后保留登录状态,强制重新登录的问题

问题根源

你当前代码中仅清除了外部登录Scheme的Cookie(IdentityConstants.ExternalScheme),但你的应用实际使用的是Identity默认的应用认证Scheme(IdentityConstants.ApplicationScheme),所以重启后客户端存储的认证Cookie依然有效,导致自动登录。同时你的LoginUser方法存在逻辑错误:无论登录成功与否都会执行跳转,可能引发异常或不符合预期的页面跳转。


解决方案1:修改登录页,清除默认认证Cookie

直接在登录页初始化时清除应用认证Scheme的Cookie,确保用户进入登录页时处于完全登出状态:

修改Login.Razor的OnInitializedAsync方法:

protected override async Task OnInitializedAsync()
{
    Input ??= new();
    ReturnUrl ??= "/";

    if (HttpMethods.IsGet(HttpContext.Request.Method))
    {
        // 清除默认应用认证Cookie,强制重新登录
        await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
        // 同时清除外部登录Cookie(如有外部登录需求)
        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
    }
}

同时修正LoginUser方法的跳转逻辑,仅在登录成功时执行跳转:

public async Task LoginUser()
{
    var result = await SignInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
    if (result.Succeeded)
    {
        SharedAppData.LoggedInThisSession = true;
        Logger.LogInformation("User logged in.");            
        // 仅登录成功时跳转
        RedirectManager.RedirectTo(ReturnUrl);
    }
    else if (result.RequiresTwoFactor)
    {
        RedirectManager.RedirectTo(
            "/Account/LoginWith2fa",
            new() { ["ReturnUrl"] = ReturnUrl, ["RememberMe"] = Input.RememberMe });
    }
    else if (result.IsLockedOut)
    {
        Logger.LogWarning("User account locked out.");
        RedirectManager.RedirectTo("/Account/Lockout");
    }
    else
    {
        errorMessage = "Error: Invalid login attempt.";
    }
}

解决方案2:全局强制应用重启后登出所有用户

如果需要无论用户是否访问登录页,只要应用重启就强制所有用户重新登录,可以通过跟踪应用启动时间,在认证时验证Cookie中的启动时间是否与当前一致:

  1. 在Program.cs中添加应用启动时间,并配置Cookie认证事件:
var builder = WebApplication.CreateBuilder(args);

// 记录应用启动时间(精确到秒,确保每次重启都不同)
var appStartTime = DateTime.UtcNow.ToString("yyyyMMddHHmmss");

// 注册服务...
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        options.LoginPath = "/Account/Login";
        options.AccessDeniedPath = "/Account/AccessDenied";

        // 登录时存储当前应用启动时间到Cookie属性
        options.Events.OnSigningIn = context =>
        {
            context.Properties.Items["AppStartTime"] = appStartTime;
            // 强制使用Session Cookie,关闭浏览器自动失效(可选)
            context.Properties.IsPersistent = false;
            return Task.CompletedTask;
        };

        // 验证Cookie时检查应用启动时间是否匹配
        options.Events.OnValidatePrincipal = context =>
        {
            var currentAppStartTime = context.HttpContext.Items["AppStartTime"] as string;
            var storedAppStartTime = context.Properties.Items.TryGetValue("AppStartTime", out var value) 
                ? value 
                : null;

            if (storedAppStartTime != currentAppStartTime)
            {
                // 应用已重启,拒绝当前认证并登出
                context.RejectPrincipal();
                return context.HttpContext.SignOutAsync(options.Cookie.Name);
            }
            return Task.CompletedTask;
        };
    });

var app = builder.Build();

// 添加中间件,将应用启动时间注入到HttpContext
app.Use(async (context, next) =>
{
    context.Items["AppStartTime"] = appStartTime;
    await next();
});

// 其他中间件配置...
app.Run();

额外说明

  • 解决方案1适合仅在用户进入登录页时强制登出,用户直接访问授权页时仍会自动跳转登录并清除Cookie;
  • 解决方案2适合全局强制,只要应用重启,所有持有旧Cookie的用户都会被强制登出,无论访问哪个页面;
  • 如果不需要RememberMe功能,可以直接在PasswordSignInAsync中将第三个参数固定为false,避免生成持久化Cookie。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 00:57:08