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

如何创建Blazor全局触发的自定义会话超时中间件

Blazor全局触发会话超时检查的解决方案

问题概述

需要在Blazor应用中,用户进行任意页面操作或调用特定类对象时自动触发会话超时检查;此前尝试通过脚本捕获点击/按键事件导致误登出,且无法使用Microsoft.Session实现超时逻辑。

现有中间件的问题分析

  1. HTTP中间件局限性:Blazor(尤其是Server模式)的交互多基于SignalR WebSocket,而非普通HTTP请求,原有中间件仅会在页面初始加载或刷新时触发,无法捕获客户端的实时交互动作。
  2. 多用户冲突风险:ATMTreeViewRepository若为单例服务,会导致所有用户共享同一个sessionTime,造成会话状态混乱。
  3. NavigationManager使用错误:中间件运行在服务器端HTTP管道中,直接获取NavigationManager(客户端服务)会引发上下文不一致问题。

解决方案步骤

1. 创建Scoped会话状态服务

用于存储每个用户独立的会话活动时间,避免多用户冲突:

public class SessionStateService
{
    // 记录最后一次活动时间
    public DateTime? LastActivityTime { get; set; }
    // 超时阈值(分钟)
    public const int TimeoutMinutes = 5;
}

在Program.cs中注册为Scoped服务:

builder.Services.AddScoped<SessionStateService>();

2. 实现会话超时管理服务

负责定时检查超时、重置会话状态,并处理导航逻辑:

public class SessionTimeoutService : IDisposable
{
    private readonly SessionStateService _sessionState;
    private readonly NavigationManager _navigationManager;
    private Timer _timeoutTimer;

    public SessionTimeoutService(SessionStateService sessionState, NavigationManager navigationManager)
    {
        _sessionState = sessionState;
        _navigationManager = navigationManager;
        // 初始化超时定时器
        ResetTimeoutTimer();
    }

    // 重置会话活动时间并重启定时器
    public void ResetSessionActivity()
    {
        _sessionState.LastActivityTime = DateTime.Now;
        ResetTimeoutTimer();
    }

    private void ResetTimeoutTimer()
    {
        _timeoutTimer?.Dispose();
        // 设置定时器:超时后触发检查,仅执行一次
        _timeoutTimer = new Timer(CheckTimeout, null, 
            TimeSpan.FromMinutes(SessionStateService.TimeoutMinutes), 
            Timeout.InfiniteTimeSpan);
    }

    private void CheckTimeout(object state)
    {
        if (_sessionState.LastActivityTime == null) return;

        var elapsedMinutes = DateTime.Now.Subtract(_sessionState.LastActivityTime.Value).TotalMinutes;
        if (elapsedMinutes > SessionStateService.TimeoutMinutes)
        {
            // 跳转到首页(强制重载以清空状态)
            _navigationManager.NavigateTo(_navigationManager.BaseUri, forceLoad: true);
        }
    }

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

注册为Scoped服务:

builder.Services.AddScoped<SessionTimeoutService>();

3. 全局捕获用户交互动作

在MainLayout.razor中添加全局事件监听,用户的点击、按键、鼠标移动操作都会重置会话状态:

@inherits LayoutComponentBase
@inject SessionTimeoutService SessionTimeoutService
@implements IDisposable

<!-- 监听全局用户交互事件 -->
<div @onclick="ResetSession" @onkeydown="ResetSession" @onmouseover="ResetSession">
    @Body
</div>

@code {
    private void ResetSession()
    {
        SessionTimeoutService.ResetSessionActivity();
    }

    public void Dispose()
    {
        ((IDisposable)SessionTimeoutService)?.Dispose();
    }
}

4. 特定类对象调用时自动触发

在需要触发超时检查的类(如ATMTreeViewRepository)中注入SessionTimeoutService,在方法执行前重置会话状态:

public class ATMTreeViewRepository
{
    private readonly SessionTimeoutService _sessionTimeoutService;

    public ATMTreeViewRepository(SessionTimeoutService sessionTimeoutService)
    {
        _sessionTimeoutService = sessionTimeoutService;
    }

    // 示例方法:调用时自动重置会话
    public async Task LoadTreeData()
    {
        _sessionTimeoutService.ResetSessionActivity();
        // 原有业务逻辑...
    }
}

方案优势

  • 避免脚本捕获事件的误触发问题:仅响应用户主动交互动作
  • 基于Scoped服务实现用户隔离:每个用户拥有独立的会话状态
  • 实时检查超时:通过定时器主动监控,无需依赖HTTP请求
  • 灵活扩展:可轻松添加更多触发场景(如特定组件交互)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 02:58:21