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

如何通过C#代码实现Blazor Server平台的用户自动登出

自动登出实现方案(兼容SignalR场景)

针对SignalR长连接导致常规登出不生效的问题,可通过以下几种方案实现数据保存完成后的自动登出,无需用户手动操作:

方案1:ASP.NET Core Identity 全链路登出(推荐)

SignalR登出不生效的核心原因是长连接不会主动校验认证凭据的有效性,因此登出时除了清除服务端认证状态,还需要主动终止对应用户的所有SignalR连接

步骤1:服务端清除认证状态

数据修改保存完成后,直接调用Identity内置的登出方法清除认证Cookie:

// 你的业务数据保存逻辑执行完成后
await _userManager.UpdateAsync(targetUser);
await _signInManager.SignOutAsync();

步骤2:维护SignalR连接映射

定义单例服务存储用户ID与SignalR连接ID的对应关系:

public class UserConnectionManager
{
    private readonly Dictionary<string, List<string>> _connectionMap = new();
    private readonly object _lockObj = new();

    public void AddConnection(string userId, string connectionId)
    {
        lock (_lockObj)
        {
            if (!_connectionMap.ContainsKey(userId))
                _connectionMap[userId] = new List<string>();
            _connectionMap[userId].Add(connectionId);
        }
    }

    public void RemoveConnection(string userId, string connectionId)
    {
        lock (_lockObj)
        {
            if (_connectionMap.TryGetValue(userId, out var connections))
            {
                connections.Remove(connectionId);
                if (connections.Count == 0)
                    _connectionMap.Remove(userId);
            }
        }
    }

    public List<string> GetUserConnections(string userId)
    {
        lock (_lockObj)
        {
            return _connectionMap.TryGetValue(userId, out var connections) 
                ? connections.ToList() 
                : new List<string>();
        }
    }
}

在Program.cs中注册为单例:

builder.Services.AddSingleton<UserConnectionManager>();

在SignalR Hub中更新连接映射:

public class AppHub : Hub
{
    private readonly UserConnectionManager _connManager;

    public AppHub(UserConnectionManager connManager)
    {
        _connManager = connManager;
    }

    public override async Task OnConnectedAsync()
    {
        var userId = Context.UserIdentifier;
        if (!string.IsNullOrEmpty(userId))
        {
            _connManager.AddConnection(userId, Context.ConnectionId);
        }
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        var userId = Context.UserIdentifier;
        if (!string.IsNullOrEmpty(userId))
        {
            _connManager.RemoveConnection(userId, Context.ConnectionId);
        }
        await base.OnDisconnectedAsync(exception);
    }
}

步骤3:登出时主动推送登出通知

登出逻辑中加入向用户所有活跃SignalR连接推送强制登出事件的代码:

var userConnections = _connManager.GetUserConnections(userId);
foreach (var connId in userConnections)
{
    await _hubContext.Clients.Client(connId).SendAsync("ForceLogout");
}

步骤4:前端自动执行登出

前端监听ForceLogout事件,无需用户点击直接完成登出跳转:

hubConnection.on('ForceLogout', () => {
    // 清理本地存储的认证信息
    localStorage.removeItem('authToken');
    sessionStorage.clear();
    // 直接跳转登录页
    window.location.href = '/Account/Login';
});

方案2:自定义认证场景登出

未使用Identity的场景可直接操作认证凭据:

  • Cookie认证场景:
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
  • JWT认证场景:
    将当前用户的有效token加入黑名单,后续请求校验时直接拒绝:
var currentToken = Request.Headers["Authorization"].ToString().Replace("Bearer ", string.Empty);
// 用缓存存储黑名单,过期时间与token有效期保持一致
_memoryCache.Set($"Blacklist:{currentToken}", true, TimeSpan.FromHours(24));

该方案需要全局添加请求过滤器,所有带JWT的请求先校验是否在黑名单中。

方案3:服务端渲染页面场景快速实现

如果是MVC/Razor Pages等服务端渲染场景,数据保存完成后直接返回登出路由的重定向结果即可:

// 数据保存完成后执行
return RedirectToAction("Logout", "Account");

Logout接口中完成登出逻辑后返回登录页跳转即可,无需额外前端代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 23:48:03