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

ASP.NET Core SignalR基础认证问题:Socket连接无法通过身份验证

解决SignalR结合Basic Auth的连接认证问题

咱们先直击核心:你的问题主要是服务端中间件顺序配错了,导致SignalR的连接请求根本没经过认证中间件,哪怕客户端带了Authorization头也白搭。另外得先搞明白:SignalR的WebSocket连接是先通过HTTP握手建立的,认证信息只需要在这个握手阶段传递就行,后续的WebSocket消息不用再带请求头——只要握手过了,整个连接就会被标记为已认证状态。

第一步:修正Startup的中间件顺序

在Configure方法里,app.UseAuthentication()必须放在app.UseSignalR()前面!你现在的代码里UseSignalR在认证中间件之前,等于SignalR的请求直接绕开了认证流程,Hub上的[Authorize]自然会拒绝连接。

修正后的Configure代码如下:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("CorsPolicy");
    // 重复的UseCors可以删掉,一个就够了
    // app.UseCors(CorsConstants.AnyOrigin);
    app.UseFileServer();
    
    // 认证中间件一定要放在SignalR前面!
    app.UseAuthentication();
    
    app.UseSignalR(route =>
    {
        route.MapHub<MessageHub>("/chat");
    });
}

ConfigureServices里的配置是没问题的,不过可以把重复的CORS配置去掉,避免冲突。

第二步:确认客户端的认证头传递

你的客户端代码已经正确在连接时添加了Basic Auth的Authorization头,这部分完全没问题:

var credential = Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("SomeUserName" + ":" + "SomePassword"));

var connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:5000/chat", options =>
    {
        options.Headers.Add("Authorization", $"Basic {credential}");
    })
    .AddJsonProtocol()
    .Build();

SignalR会把这里配置的头放到握手的HTTP请求里发送,服务端只要中间件顺序对了,就会自动验证这个头,通过后[Authorize]就会放行连接。

关于"Socket消息携带令牌"的补充方案

如果你之后想换成自定义令牌(比如JWT),或者需要在连接建立后再做验证,这里给两个可选方案:

方案1:用Bearer Token(推荐)

把Basic Auth换成JWT认证,客户端在握手时传递Authorization: Bearer {你的JWT令牌}头,服务端配置JWT认证中间件——同样要保证中间件顺序在SignalR之前,逻辑和Basic Auth类似,只是认证方式换了。

方案2:连接后发送身份信息验证

如果必须在连接建立后验证身份,可以在Hub里加一个Authenticate方法,客户端连接成功后立刻调用这个方法传令牌,验证不通过就直接断开连接:

服务端Hub修改:

[AllowAnonymous]
public class MessageHub : Hub
{
    private static readonly HashSet<string> ConnectedIds = new HashSet<string>();
    private static readonly HashSet<string> AuthenticatedIds = new HashSet<string>();

    public override async Task OnConnectedAsync()
    {
        ConnectedIds.Add(Context.ConnectionId);
        await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
    }

    public async Task Authenticate(string token)
    {
        // 这里写你的令牌验证逻辑,比如查数据库/缓存
        if (IsValidToken(token))
        {
            AuthenticatedIds.Add(Context.ConnectionId);
            await Clients.Caller.SendAsync("AuthenticationSuccess");
        }
        else
        {
            await Clients.Caller.SendAsync("AuthenticationFailed");
            await Context.Connection.AbortAsync();
        }
    }

    public override async Task OnDisconnectedAsync(Exception ex)
    {
        ConnectedIds.Remove(Context.ConnectionId);
        AuthenticatedIds.Remove(Context.ConnectionId);
        await Clients.All.SendAsync("SendAction", "left", ConnectedIds.Count);
    }

    // 可以用[Authorize]或者自己检查是否在已认证列表里
    public async Task Send(MessagePayload message)
    {
        if (!AuthenticatedIds.Contains(Context.ConnectionId))
        {
            throw new HubException("请先完成认证");
        }
        await Clients.All.SendAsync("SendMessage", message);
    }

    private bool IsValidToken(string token)
    {
        // 替换成你的实际验证逻辑
        return token == "your-valid-token";
    }
}

客户端调用认证:

await connection.StartAsync();
// 连接成功后马上发认证请求
await connection.InvokeAsync("Authenticate", "your-token");

不过这个方案不如握手阶段认证高效安全——握手失败直接就拒绝连接了,不用建立连接后再断开。

验证修复效果

改完中间件顺序后重启服务端和客户端,应该就能正常连接了。你还可以在Hub里通过Context.User拿到认证后的用户信息,比如:

public override async Task OnConnectedAsync()
{
    var userName = Context.User.Identity.Name;
    // 可以在这里处理用户的连接逻辑,比如记录在线用户
    ConnectedIds.Add(Context.ConnectionId);
    await Clients.All.SendAsync("SendAction", "joined", ConnectedIds.Count);
}

内容的提问来源于stack exchange,提问作者Node.JS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:57:29