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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 01:24:32