如何基于Entra用户ID通过SignalR/Azure SignalR推送指定用户通知?
一、SignalR通过Entra ID区分用户并推送
你示例中直接用Client(entraUserId)的写法不可行,因为Client()方法仅接受connectionId,但SignalR支持用户标识映射机制,可将Entra用户ID关联到SignalR用户上下文,之后就能直接通过用户ID推送通知,无需手动处理connectionId。
实现步骤:
- 配置SignalR认证与用户标识提取
在SignalR服务端(如ASP.NET Core)完成Entra认证配置后,需从认证票据中提取Entra用户的Object ID(对应Claim类型为http://schemas.microsoft.com/identity/claims/objectidentifier,即你提到的GUID格式用户ID),并通过自定义IUserIdProvider将其设置为SignalR的用户ID:
services.AddSignalR() .AddAzureSignalR(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(options => { // 配置Entra认证相关选项 }, options => { // 配置Azure AD租户等基础选项 }); // 注册自定义用户ID提供者 services.AddSingleton<IUserIdProvider, EntraUserIdProvider>(); public class EntraUserIdProvider : IUserIdProvider { public string GetUserId(HubConnectionContext connection) { // 从用户Claims中提取Entra Object ID return connection.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; } }
- 通过用户ID推送通知
完成配置后,可使用Users()方法替代Client(),直接针对Entra用户ID列表推送:
foreach(var entraUserId in notification.Users) { await this.hubContext.Users(entraUserId).SendAsync("ReceiveNotification", message); }
Users()方法会自动匹配所有关联该用户ID的活跃连接,批量推送通知,无需手动维护connectionId与用户ID的映射关系。
二、Entra用户ID与connectionId的手动映射(可选)
如果需要额外管理连接状态,可通过SignalR的连接事件手动维护映射关系:
- 在Hub类中监听
OnConnectedAsync和OnDisconnectedAsync事件,记录用户ID与connectionId的对应关系:
public class NotificationHub : Hub { private static readonly Dictionary<string, List<string>> UserConnectionMap = new(); public override async Task OnConnectedAsync() { var userId = Context.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; if (!string.IsNullOrEmpty(userId)) { lock (UserConnectionMap) { if (!UserConnectionMap.ContainsKey(userId)) { UserConnectionMap[userId] = new List<string>(); } UserConnectionMap[userId].Add(Context.ConnectionId); } } await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception exception) { var userId = Context.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; if (!string.IsNullOrEmpty(userId)) { lock (UserConnectionMap) { if (UserConnectionMap.ContainsKey(userId)) { UserConnectionMap[userId].Remove(Context.ConnectionId); if (UserConnectionMap[userId].Count == 0) { UserConnectionMap.Remove(userId); } } } } await base.OnDisconnectedAsync(exception); } }
之后可通过该字典获取指定Entra用户对应的所有connectionId,再调用Client(connectionId)推送通知。
三、Azure Function + Azure SignalR + Service Bus的场景实现
当Service Bus触发Function App时,完全可以向特定Entra用户ID推送通知,核心是利用Azure SignalR的服务端推送能力与用户标识映射:
实现步骤:
配置Function与Azure SignalR集成
在Function App中安装Microsoft.Azure.WebJobs.Extensions.SignalRService包,并配置Azure SignalR服务的连接字符串。关联用户标识
Angular客户端连接SignalR时需携带Entra认证Token,Azure SignalR会自动解析Token中的用户Claim,你也可在Function中通过自定义逻辑提取Entra Object ID作为用户标识。Service Bus触发的推送逻辑
在Service Bus触发的Function中,使用SignalRAsyncCollector向指定用户推送通知:
[FunctionName("NotifyUsersFromServiceBus")] public static async Task Run( [ServiceBusTrigger("notification-queue", Connection = "ServiceBusConnection")] string notificationJson, [SignalR(HubName = "NotificationHub")] IAsyncCollector<SignalRMessage> signalRMessages, ILogger log) { var notification = JsonSerializer.Deserialize<Notification>(notificationJson); foreach (var entraUserId in notification.Users) { await signalRMessages.AddAsync(new SignalRMessage { UserId = entraUserId, Target = "ReceiveNotification", Arguments = new[] { notification.Message } }); } }
这里直接将Entra用户ID传入UserId字段即可,Azure SignalR会自动匹配该用户的所有活跃连接并推送通知。
需注意,Function App需配置Entra认证权限,确保能正确解析客户端Token并关联用户标识。
内容的提问来源于stack exchange,提问作者Ignacio Iron Babbini

