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

Abh*_*eet 8 c# azure signalr 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果是,Asp.Net Identity Claims Principal 如何传递给 Azure SignalR?

编辑:2021 年 6 月 18 日

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

这个问题与 SignalR 的处理方式有关.Users(<*some-user-id*>).Send()

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

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

Azure SignalR 是否在内部跟踪 UserId、Connection Id 映射并发送消息?

Ale*_*kin 2

是的,如果您想实现无服务器架构,您应该使用 Azure Functions。典型的无服务器架构如下所示

Azure 信号器架构

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

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

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

上游设置

SendMessage除了 Azure 函数绑定之外,a 中方法的代码hub与常规集线器非常相似。您还需要扩展ServerlessHub类而不是Hub安装Microsoft.Azure.WebJobs.Extensions.SignalRService

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:

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

网络应用程序架构

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

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

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

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

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

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

这一切都在这篇文章中描述了

您可以在此处找到有关实施身份验证的详细指南