如何通过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
相关产品推荐
相关产品推荐

