SignalR Core如何向用户告知未授权的Hub方法调用?
我正在使用SignalR Core开发Web应用,通过不带角色的[Authorize]确保仅已认证用户可连接Hub。部分方法需要特定策略,因此给这些方法添加了带特定策略的[Authorize]属性,Hub代码如下:
[Authorize] public class CustomerHub : Hub { public async Task SimpleMethod() { // 无需额外授权的操作 await Task.CompletedTask; } [Authorize(Policies.IsAdmin)] public async Task AdvancedMethod() { // 需要管理员权限的操作 await Task.CompletedTask; } public async Task ErrorMethod() { await Task.CompletedTask; throw new NotImplementedException(); } }
当前逻辑运行正常:已认证用户可连接Hub并调用SimpleMethod,权限不足时不会进入AdvancedMethod。但用户未授权调用特定方法时,客户端收不到任何错误提示。
我已实现自定义IHubFilter用于向用户通知错误,但发现AuthorizeAttribute的评估早于该HubFilter的执行,代码如下:
public class CustomHubExceptionsFilter : IHubFilter { public async ValueTask<object?> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object?>> next) { try { var result = await next(invocationContext); return result; } catch (Exception ex) { await invocationContext.Hub.Clients.Caller.SendCoreAsync("Error", new object?[] { $"Oops, something went wrong. Technical details: {ex.GetType().Name}" }); throw; } } }
我也曾尝试添加自定义AuthorizationHandler,但未授权时会直接关闭连接,这不是理想方案。请问:
- 是否有借助
AuthorizeAttribute告知用户的方法? - 若需自定义
IHubFilter检查授权,有没有更简洁的原生方式,避免大量自定义代码?
SignalR原生没有直接通过AuthorizeAttribute返回客户端错误的机制——授权检查在Hub方法执行(包括HubFilter)前就完成了,未通过授权时不会触发后续流程,自然也不会进入你的异常捕获Filter。下面提供两种可行的简洁方案:
方案1:手动策略检查(轻量灵活)
保留原有Hub的授权结构,仅对需要错误提示的方法,手动注入IAuthorizationService做策略检查,不通过时主动向客户端发送错误:
[Authorize] public class CustomerHub : Hub { private readonly IAuthorizationService _authorizationService; // 构造函数注入IAuthorizationService public CustomerHub(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } public async Task SimpleMethod() { await Task.CompletedTask; } public async Task AdvancedMethod() { // 手动检查IsAdmin策略 var authResult = await _authorizationService.AuthorizeAsync(Context.User, null, Policies.IsAdmin); if (!authResult.Succeeded) { // 向客户端发送授权错误提示 await Clients.Caller.SendCoreAsync("Error", new object[] { "权限不足,无法执行此操作" }); return; } // 执行管理员操作 await Task.CompletedTask; } public async Task ErrorMethod() { await Task.CompletedTask; throw new NotImplementedException(); } }
优势:代码量少,无需修改全局配置,仅针对需要提示的方法处理,不会触发连接关闭。
方案2:全局授权过滤器(统一处理)
如果需要全局统一处理所有Hub方法的授权错误,可以自定义一个IAuthorizationFilter,在授权失败时主动发送错误并阻止默认的连接关闭行为:
public class HubAuthorizationFilter : IAuthorizationFilter { private readonly IAuthorizationService _authorizationService; public HubAuthorizationFilter(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { if (context.Resource is HubInvocationContext hubContext) { // 获取当前方法的所有授权策略 var policies = context.Filters.OfType<AuthorizeAttribute>().SelectMany(a => a.Policies); var authResult = await _authorizationService.AuthorizeAsync(hubContext.Context.User, null, policies); if (!authResult.Succeeded) { // 发送授权错误给客户端 await hubContext.Hub.Clients.Caller.SendCoreAsync("Error", new object[] { "权限不足,无法执行此操作" }); // 设置结果为已处理,避免默认的连接关闭 context.Result = new AuthorizationResult(); } } } }
然后在Program.cs中注册这个过滤器:
builder.Services.AddSignalR(options => { options.AddFilter<HubAuthorizationFilter>(); });
优势:全局生效,无需逐个修改Hub方法,统一处理所有授权失败场景。
客户端配套处理
无论使用哪种方案,都需要确保客户端注册Error事件监听,以接收并展示错误提示,示例JavaScript客户端代码:
connection.on('Error', (message) => { console.error('SignalR错误:', message); // 这里可以将错误展示给用户,比如弹出提示框 alert(message); });
内容的提问来源于stack exchange,提问作者Daan

