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

如何实现Blazor Server应用认证过期自动跳转至登录页

解决方案:Blazor Server认证过期自动跳转至登录页

方案1:组件级监控(最直接可控)

创建独立的AuthMonitor.razor组件嵌入全局布局,利用组件生命周期管理定时器,避免被提前释放:

@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavManager
@implements IDisposable
@using System.Security.Claims
@using YourNamespace.Constants // 替换为你的Constants命名空间

@code {
    private Timer _authCheckTimer;
    private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(2); // 按需调整检查频率

    protected override async Task OnInitializedAsync()
    {
        _authCheckTimer = new Timer(async _ => await CheckAuthState(), null, TimeSpan.Zero, _checkInterval);
        await base.OnInitializedAsync();
    }

    private async Task CheckAuthState()
    {
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity?.IsAuthenticated == true)
        {
            var expiryClaim = user.FindFirst(ClaimTypes.Expiration);
            if (expiryClaim != null && DateTime.TryParse(expiryClaim.Value, out var expiryTime))
            {
                if (DateTime.UtcNow >= expiryTime)
                {
                    // 确保在Blazor UI线程执行导航,避免跨线程异常
                    await InvokeAsync(() => 
                    {
                        NavManager.NavigateTo(Constants.Routes.LOGIN, true);
                    });
                }
            }
        }
    }

    public void Dispose()
    {
        _authCheckTimer?.Dispose();
    }
}

在MainLayout.razor中引入该组件:

<div class="main">
    <div class="content px-4">
        @Body
    </div>
</div>
<AuthMonitor /> <!-- 全局监控组件 -->

方案2:客户端JS定时器(更稳定)

利用浏览器JS定时器监控过期时间,不受Blazor组件生命周期影响:

  1. 在wwwroot/js/auth-utils.js中添加JS逻辑:
window.authUtils = {
    setExpiryRedirect: function(expirySeconds, loginPath) {
        setTimeout(() => {
            window.location.href = loginPath;
        }, expirySeconds * 1000);
    }
};
  1. 登录成功后调用JS设置定时器:
// 登录代码片段
DateTime expiry = DateTime.UtcNow.AddSeconds(10);
var claims = new List<Claim>()
{
    new(ClaimTypes.Name, Constants.DEFAULT_AUTH_USER),
    new(ClaimTypes.Role, Constants.DEFAULT_AUTH_ROLE),
    new(ClaimTypes.Expiration, expiry.ToString("o", CultureInfo.InvariantCulture))
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));

// 计算剩余有效时间(留1秒缓冲)
var remainingSeconds = (int)(expiry - DateTime.UtcNow).TotalSeconds - 1;
await JSRuntime.InvokeVoidAsync("window.authUtils.setExpiryRedirect", remainingSeconds, Constants.Routes.LOGIN);

NavigationManager.NavigateTo("/", true);
  1. 在_Host.cshtml中引入JS文件:
<script src="~/js/auth-utils.js"></script>

方案3:自定义AuthenticationStateProvider触发状态变化

改进CircuitHandler方案,通过自定义认证状态提供者主动推送过期事件:

  1. 实现自定义认证状态提供者:
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

public class ExpiryAuthStateProvider : RevalidatingServerAuthenticationStateProvider
{
    private readonly IServiceScopeFactory _scopeFactory;

    public ExpiryAuthStateProvider(ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory) 
        : base(loggerFactory)
    {
        _scopeFactory = scopeFactory;
    }

    protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(2);

    protected override async Task<bool> ValidateAuthenticationStateAsync(
        AuthenticationState authenticationState, CancellationToken cancellationToken)
    {
        var user = authenticationState.User;
        if (!user.Identity.IsAuthenticated)
            return true;

        var expiryClaim = user.FindFirst(ClaimTypes.Expiration);
        if (expiryClaim != null && DateTime.TryParse(expiryClaim.Value, out var expiryTime))
        {
            if (DateTime.UtcNow >= expiryTime)
            {
                // 通知所有订阅者认证状态变化
                NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
                return false;
            }
        }
        return true;
    }
}
  1. 注册服务:
builder.Services.AddScoped<AuthenticationStateProvider, ExpiryAuthStateProvider>();
  1. 在MainLayout.razor中监听状态变化并跳转:
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavManager
@implements IDisposable
@using YourNamespace.Constants

@code {
    private IDisposable _authSubscription;

    protected override void OnInitialized()
    {
        _authSubscription = AuthStateProvider.AuthenticationStateChanged += HandleAuthStateChanged;
        base.OnInitialized();
    }

    private async void HandleAuthStateChanged(Task<AuthenticationState> task)
    {
        var authState = await task;
        if (!authState.User.Identity.IsAuthenticated)
        {
            await InvokeAsync(() => 
            {
                NavManager.NavigateTo(Constants.Routes.LOGIN, true);
            });
        }
    }

    public void Dispose()
    {
        _authSubscription?.Dispose();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:40:52