使用 Azure SignalR 向特定用户发送消息

Send Message to Specific user using Azure SignalR

使用Azure SignalR(不是.Net Core SignalR),Clients.Users.SendAsync是否支持向特定用户发送消息?

[Authorize]
public class ChatHub : Hub
{
    public async Task OnNewMessage(string userId, string message)
    {
        await Clients.Users(userId) 
             //Does Azure SignalR supports sending message to specific user,
             // the way .Net Core SignalR does it
            .SendAsync("OnNewMessage", userId, message);
    }
}

If yes, How Asp.Net Identity Claims Principal gets passed to Azure SignalR?

编辑:2021 年 6 月 18 日

以下回复并未解决问题。抱歉,我已经按照文档自定义 UserIdBasedProvider 等。我想强调一下 -

This question is pertaining to how SignalR handles .Users(<*some-user-id*>).Send();

理想情况下我们这样做 .Client(<connection id>).Send() 向特定用户发送消息。但是对于这种架构,我们必须自己将userId和connectionId存储在DB等中

.User(userId).Send() 与 .Net Core SignalR 完美配合。所以我相信 SignalR SDK 一定一直在内存中保存每个连接 ID 和用户 ID 的映射。

Does Azure SignalR internally keeps track of UserId, Connection Id mapping and sends the message?

您可以通过像这样实现自定义 IUserIdProvider 并将其注册为单例来实现。

 public class UserIdProvider : IUserIdProvider
    {
        public string GetUserId(HubConnectionContext connection)
        {
            return connection.User.FindFirst(ClaimConstants.PreferredUserName).Value;
        }
    }

相关Reference and Github

是的,如果你想实现无服务器架构,你应该为此使用 Azure Functions。典型的无服务器架构看起来像这样

客户端(即浏览器)向 Azure 函数发送协商请求,响应包含与 Azure SignalR 建立连接所需的所有信息。您应该将 JWT 令牌附加到协商请求。如何获取 JWT 令牌取决于您。附加它非常简单,只需在创建 signalR 连接时提供一个令牌工厂即可

const connection = new signalR.HubConnectionBuilder()
    .withUrl('link to your azure functions api', {
        accessTokenFactory: () => {
            return 'your token'
        }
    })
    .build();

为此,您还需要设置一个 upstream 连接,以便您的 Azure SignalR 服务可以将所有事件转发到您的 Azure 函数。 Azure 函数处理上游事件并将请求发送到 Azure SignalR 以向给定用户或多个用户广播数据。

hubSendMessage 方法的代码与常规集线器非常相似,但 Azure 函数绑定除外。您还需要扩展 ServerlessHub class 而不是 Hub 并安装 Microsoft.Azure.WebJobs.Extensions.SignalRService package

public class ChatHub : ServerlessHub
{

    [FunctionName(nameof(SendToUser))]
    public async Task SendToUser([SignalRTrigger] InvocationContext invocationContext, string userName, string message)
    {
        await Clients.User(userName).SendAsync("new_message", new NewMessage(invocationContext, message));
    }

    [FunctionName("negotiate")]
    public SignalRConnectionInfo Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req)
    {
        var claims = GetClaims(req.Headers["Authorization"]);
        var userName = claims.First(x => x.Type == ClaimTypes.NameIdentifier);
        return Negotiate(userName.Value, claims);
    }


    private class NewMessage
    {
        public string ConnectionId { get; }
        public string Sender { get; }
        public string Text { get; }

        public NewMessage(InvocationContext invocationContext, string message)
        {
            Sender = string.IsNullOrEmpty(invocationContext.UserId) ? string.Empty : invocationContext.UserId;
            ConnectionId = invocationContext.ConnectionId;
            Text = message;
        }
    }
}

您还可以找到包含分步说明的代码示例 here

编辑:

如果您不想完全无服务器并想使用常规网络应用程序,那也很好。架构图看起来像这样

在这种情况下,您应该使用常规集线器 class。您发布的代码应该可以正常工作。

连接管理完全由 Azure Signalr 服务完成,您不需要进行任何额外的同步,也不需要将连接数据存储在数据库中。只需设置层大小和单位数量。

以下是其工作原理的简短描述:

  1. 客户端(例如浏览器中的 Js 客户端)向 negotiate 端点发送对 Web 应用程序的请求,并接收 link 到 azure signalr 服务和访问令牌。这是一个 JWT 令牌。 JWT 令牌由几个部分组成。该令牌的第二部分是负载,其中包含声明。
  2. 使用从 negotiate 端点客户端获取的数据连接到 azure signalr 服务。

因此,Azure SignalR 服务使用此访问令牌(它从协商端点接收)来对用户进行身份验证。您无需编写任何额外的代码,因为这一切都由库在幕后完成。

如果您使用网络应用,negotiate 请求由 SignalR Sdk 处理,您无需为此编写任何额外代码。

this article

中都有描述

您还可以找到有关实施身份验证的详细指南here