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

.NET 9 Blazor Server会话失效/过期检测及即时重定向实现需求

Blazor Server会话失效即时重定向解决方案

针对Blazor Server应用会话失效后无法及时重定向导致的交互卡顿、数据丢失问题,结合ASP.NET Core Identity和Cookie认证特性,可通过以下方案实现即时重定向至/inactive-session页面:

方案1:服务器端Circuit检测+SignalR推送

Blazor Server基于SignalR维护客户端与服务器的Circuit连接,通过自定义CircuitHandler跟踪会话状态,一旦检测到认证失效,立即向客户端推送重定向指令。

实现步骤:

  • 自定义CircuitHandler,注入IHttpContextAccessor和AuthenticationStateProvider跟踪认证状态:
public class SessionMonitoringCircuitHandler : CircuitHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly AuthenticationStateProvider _authStateProvider;
    private Timer _sessionCheckTimer;

    public SessionMonitoringCircuitHandler(IHttpContextAccessor httpContextAccessor, AuthenticationStateProvider authStateProvider)
    {
        _httpContextAccessor = httpContextAccessor;
        _authStateProvider = authStateProvider;
    }

    public override async Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        // 启动定时检测任务,间隔建议设为比滑动过期时间短5分钟
        _sessionCheckTimer = new Timer(async _ => await CheckSessionStatus(circuit), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
        await base.OnConnectionUpAsync(circuit, cancellationToken);
    }

    private async Task CheckSessionStatus(Circuit circuit)
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        if (!authState.User.Identity.IsAuthenticated)
        {
            await circuit.SendAsync("RedirectToInactiveSession");
            _sessionCheckTimer?.Dispose();
            return;
        }

        // 验证Cookie是否已过期(适配滑动过期场景)
        var authCookie = _httpContextAccessor.HttpContext?.Request.Cookies[".AspNetCore.Identity.Application"];
        if (authCookie != null)
        {
            var cookieOptions = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<IOptions<CookieAuthenticationOptions>>().Value;
            var ticket = cookieOptions.TicketDataFormat.Unprotect(authCookie);
            if (ticket?.Properties.ExpiresUtc < DateTimeOffset.UtcNow)
            {
                await circuit.SendAsync("RedirectToInactiveSession");
                _sessionCheckTimer?.Dispose();
            }
        }
    }

    public override async Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        _sessionCheckTimer?.Dispose();
        await base.OnConnectionDownAsync(circuit, cancellationToken);
    }
}
  • 在Program.cs中注册CircuitHandler:
builder.Services.AddScoped<CircuitHandler, SessionMonitoringCircuitHandler>();
  • 在MainLayout.razor中注册SignalR消息监听:
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable

@code {
    private IDisposable _redirectSubscription;

    protected override async Task OnInitializedAsync()
    {
        var hubConnection = await JSRuntime.InvokeAsync<HubConnection>("Blazor.platform.getHubConnection");
        _redirectSubscription = hubConnection.On("RedirectToInactiveSession", async () =>
        {
            await JSRuntime.InvokeVoidAsync("window.location.href", "/inactive-session");
        });
        await base.OnInitializedAsync();
    }

    public async ValueTask DisposeAsync()
    {
        _redirectSubscription?.Dispose();
        await Task.CompletedTask;
    }
}

方案2:客户端定时心跳检测

客户端定期向服务器发送轻量级请求验证会话有效性,一旦检测失效立即重定向,实现简单且不依赖服务器推送逻辑。

实现步骤:

  • 创建会话验证API端点:
[ApiController]
[Route("api/[controller]")]
public class SessionController : ControllerBase
{
    [HttpGet("validate")]
    public IActionResult ValidateSession()
    {
        return User.Identity.IsAuthenticated ? Ok() : Unauthorized();
    }
}
  • 在MainLayout.razor中添加定时检测逻辑:
@inject HttpClient HttpClient
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable

@code {
    private Timer _heartbeatTimer;

    protected override void OnInitialized()
    {
        // 每分钟检测一次,可根据业务调整间隔
        _heartbeatTimer = new Timer(async _ => await CheckSession(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
        base.OnInitialized();
    }

    private async Task CheckSession()
    {
        try
        {
            var response = await HttpClient.GetAsync("/api/session/validate");
            if (!response.IsSuccessStatusCode)
            {
                await JSRuntime.InvokeVoidAsync("window.location.href", "/inactive-session");
                _heartbeatTimer?.Dispose();
            }
        }
        catch (HttpRequestException)
        {
            // 网络问题导致无法连接,直接重定向
            await JSRuntime.InvokeVoidAsync("window.location.href", "/inactive-session");
            _heartbeatTimer?.Dispose();
        }
    }

    public async ValueTask DisposeAsync()
    {
        _heartbeatTimer?.Dispose();
        await Task.CompletedTask;
    }
}

方案3:客户端Cookie过期检测

利用认证Cookie的过期时间,客户端JS定时检查剩余有效期,快过期时直接重定向,同时配合服务器端验证避免客户端篡改。

实现步骤:

  • 在_Host.cshtml中添加JS逻辑:
<script>
    function startSessionMonitoring() {
        setInterval(() => {
            const authCookie = document.cookie.split('; ').find(row => row.startsWith('.AspNetCore.Identity.Application='));
            if (!authCookie) {
                window.location.href = '/inactive-session';
                return;
            }

            // 解析Cookie过期时间(实际场景需匹配Cookie认证的序列化规则)
            const expiresMatch = document.cookie.match(/(^|;) ?.AspNetCore.Identity.Application=.*?; expires=([^;]+)/);
            if (expiresMatch && new Date(expiresMatch[2]) < new Date()) {
                window.location.href = '/inactive-session';
            }
        }, 60000); // 每分钟检测一次
    }

    window.addEventListener('load', startSessionMonitoring);
</script>
  • 关键操作前需补充服务器端认证状态检查,避免客户端篡改Cookie导致的误判。

注意事项

  • 滑动过期场景下,需确保服务器端Cookie刷新逻辑正常,避免客户端检测与服务器状态不一致。
  • 定时检测间隔需根据会话过期时间合理设置,过短增加服务器负载,过长则无法及时检测失效。
  • 重定向时需确保Blazor Circuit已正确释放,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 00:03:09