.NET 6中SignalR的AuthorizeHubConnection未触发问题求助
问题描述
尝试在.NET 6中基于SignalR实现自定义授权方案(不使用JWT,采用类似PAT的方式),但重写的AuthorizeHubConnection方法从未被调用。相关代码如下:
Server端配置(Program.cs)
builder.Services.AddSignalR(o => { o.EnableDetailedErrors = true; o.ClientTimeoutInterval = TimeSpan.FromSeconds(5); }); ... app.UseAuthentication(); app.UseAuthorization(); ... // SignalR Hub app.MapHub<ServerHub>("/serverhub", options => { options.Transports = HttpTransportType.LongPolling; });
自定义授权特性(HubAuthorizationAttribute.cs)
using System.Security.Claims; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Microsoft.AspNetCore.Mvc.Filters; namespace Hub.API.Authorization { public class HubAuthorizationAttribute : Microsoft.AspNet.SignalR.AuthorizeAttribute { public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request) { var incomingClientId = request.QueryString.Where(k => k.Key.Equals("clientId")).Select(v => v.Value); var user = request.User.Identity.Name; return base.AuthorizeHubConnection(hubDescriptor, request); } public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod) { Console.WriteLine("Inside AuthorizeHubMethodInvokation"); return base.AuthorizeHubMethodInvocation(hubIncomingInvokerContext, appliesToMethod); } } }
ServerHub.cs
[HubAuthorization] public sealed class ServerHub: Hub { ...// 省略其他代码 }
.NET客户端代码
var hubConnection = new HubConnectionBuilder() .WithUrl($"https://localhost:63929/agenthub?clientid={clientId}", options => { //options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.LongPolling; }) .WithAutomaticReconnect() .ConfigureLogging(logging => { logging.AddConsole(); // This will set ALL logging to Debug level logging.SetMinimumLevel(LogLevel.Debug); }) .Build(); //Signal R Registration hubConnection.On<bool>("AgentRegistrationStatusReceived", (isReachable) => { if (isReachable) Console.WriteLine("(+) Agent is registered!!"); else Console.WriteLine("(+) Agent wasn't able to be registered!"); }); // SignalR GetState Request from API Server hubConnection.On<string, Guid>("GetState", async (callbackMethod, requestId) => { Console.WriteLine("Receiving GetState request from server"); bool isAgentAvailable = await LongPollingTest.GetAgentState(); Console.WriteLine("Sending GetState response to server"); await hubConnection.InvokeAsync(callbackMethod, requestId, isAgentAvailable); }); hubConnection.StartAsync().Wait();
解决方案
核心问题是你引用了错误的SignalR授权特性:你继承的是Microsoft.AspNet.SignalR.AuthorizeAttribute(这是ASP.NET框架下的老版本SignalR),而.NET 6使用的是ASP.NET Core SignalR,对应的命名空间是Microsoft.AspNetCore.SignalR,老版本的特性在ASP.NET Core环境中不会被触发。
以下是两种正确的实现方式:
方式1:使用自定义AuthorizationHandler(推荐)
1. 定义自定义授权需求
public class HubPatRequirement : IAuthorizationRequirement { // 可添加自定义验证规则参数 }
2. 实现授权处理器
public class HubPatHandler : AuthorizationHandler<HubPatRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HubPatRequirement requirement) { // 获取SignalR连接上下文 if (context.Resource is HubInvocationContext hubContext) { // 从QueryString提取clientId(即你的PAT) if (hubContext.HttpContext.Request.Query.TryGetValue("clientId", out var clientIdValues) && !string.IsNullOrEmpty(clientIdValues.FirstOrDefault())) { var clientId = clientIdValues.First(); // 替换为你的PAT验证逻辑:比如从数据库/配置校验有效性 bool isValid = ValidateClientId(clientId); if (isValid) { // 验证通过,添加用户标识 var claims = new List<Claim> { new Claim(ClaimTypes.Name, clientId) }; var identity = new ClaimsIdentity(claims, "PAT"); context.User.AddIdentity(identity); context.Succeed(requirement); } } } return Task.CompletedTask; } // 模拟PAT验证逻辑 private bool ValidateClientId(string clientId) { return clientId == "valid-pat-token"; } }
3. 注册授权服务
在Program.cs中添加:
builder.Services.AddAuthorization(options => { options.AddPolicy("HubPatPolicy", policy => { policy.Requirements.Add(new HubPatRequirement()); }); }); // 注册自定义处理器 builder.Services.AddScoped<IAuthorizationHandler, HubPatHandler>();
4. 在Hub上应用授权策略
[Authorize(Policy = "HubPatPolicy")] public sealed class ServerHub : Hub { // 你的Hub方法逻辑 }
方式2:在OnConnectedAsync中直接验证
如果不需要复杂的授权策略,可直接在Hub的连接方法中做验证:
public sealed class ServerHub : Hub { public override async Task OnConnectedAsync() { if (Context.GetHttpContext().Request.Query.TryGetValue("clientId", out var clientIdValues)) { var clientId = clientIdValues.FirstOrDefault(); if (string.IsNullOrEmpty(clientId) || !ValidateClientId(clientId)) { // 验证失败,断开连接 await Context.AbortAsync(); return; } // 验证通过,继续处理连接 await base.OnConnectedAsync(); } else { // 无clientId参数,直接断开 await Context.AbortAsync(); } } private bool ValidateClientId(string clientId) { // 替换为实际验证逻辑 return clientId == "valid-pat-token"; } }
额外注意事项
- 客户端连接路径需与服务端一致:你的Server端
MapHub路径是/serverhub,但客户端连接的是/agenthub,需统一路径。 - 长轮询配置需两端一致:可在客户端显式设置
options.Transports = HttpTransportType.LongPolling;。
内容的提问来源于stack exchange,提问作者deathcat05
相关产品推荐
相关产品推荐

