ASP.NET 核心 Angular - 根据登录用户发送不同的 SignalR 消息
ASP.NET Core Angular - send different SignalR messages based on logged in user
我有 Angular SPA ASP.NET 带有身份的核心应用程序 (IdentityServer4)。我使用 SignalR 向客户端推送实时消息。
但是我必须“广播”消息。无论他们需要什么,所有客户端都会收到相同的消息,然后他们会在 Typescript 中弄清楚——他们是否需要这条消息。
我想要的是能够决定哪个 SignalR 客户端应该接收消息以及什么内容 - 这将使消息更短并完全减少客户端的处理时间。
我看到有 hub.Client.User(userId)
方法 - 这就是我需要的。但是,SignalR 似乎不知道身份用户 ID。
如果我覆盖 public override Task OnConnectedAsync()
- 内部上下文没有任何有用的信息,例如 user/principals/claims - 是空的。
如何找出哪个 IdentityServer4
用户正在连接到集线器?
EDIT1 建议实施 IUserIdProvider 不起作用 - 所有 xs 均为空。
public string GetUserId(HubConnectionContext connection)
{
var x1 = connection.User?.FindFirst(ClaimTypes.Email)?.Value;
var x2 = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var x3 = connection.User?.FindFirst(ClaimTypes.Name)?.Value;
...
EDIT2 从 https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0 实施了“身份服务器 JWT 身份验证” - 也不起作用 - accessToken
在 PostConfigure
[=20 中为空=]
您需要实施 IUserIdProvider 并将其注册到服务集合中。
检查这个问题 -
据了解,SignalR 服务器上下文中的用户数据为空,因为授权未按我的预期工作。 使用 Identity Server 实现 SignalR 授权似乎是一件大事,并且存在安全风险,因为它会影响整个应用程序 - 您基本上需要手动覆盖 Identity Server 已经完成的大量代码,只是为了满足 SignalR case.
所以我想出了一个 解决方法,请在此处查看我对自己的回答:
编辑:我错过了一个明显的解决方案 - 请参阅其他答案。不过,这仍然是有效的解决方法,所以我打算让它挂在这里。
有一个明显的解决方案。这是在创建具有身份的 asp.net 核心 angular 应用程序后可以使用的示例。
请注意,在这个特定场景中(Angular with ASP.NET Core with Identity)你 NOT 需要实现任何其他,与人们误读文档的多项建议相反:https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0
客户端:
import { AuthorizeService } from '../../api-authorization/authorize.service';
. . .
constructor(. . . , authsrv: AuthorizeService) {
this.hub = new HubConnectionBuilder()
.withUrl("/newshub", { accessTokenFactory: () => authsrv.getAccessToken().toPromise() })
.build();
服务器端:
[Authorize]
public class NewsHub : Hub
{
public static readonly SortedDictionary<string, HubAuthItem> Connected = new SortedDictionary<string, HubAuthItem>();
public override Task OnConnectedAsync()
{
NewsHub.Connected.Add(Context.ConnectionId, new HubAuthItem
{
ConnectionId = Context.ConnectionId,
UserId = Context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value
});
return base.OnConnectedAsync();
}
}
这样使用:
if(NewsHub.Connected.Count != 0)
foreach (var cnn in NewsHub.Connected.Values.Where(i => !string.IsNullOrEmpty(i.UserId)))
if(CanSendMessage(cnn.UserId)
hub.Clients.Users(cnn.UserId).SendAsync("servermessage", "message text");
我有 Angular SPA ASP.NET 带有身份的核心应用程序 (IdentityServer4)。我使用 SignalR 向客户端推送实时消息。
但是我必须“广播”消息。无论他们需要什么,所有客户端都会收到相同的消息,然后他们会在 Typescript 中弄清楚——他们是否需要这条消息。
我想要的是能够决定哪个 SignalR 客户端应该接收消息以及什么内容 - 这将使消息更短并完全减少客户端的处理时间。
我看到有 hub.Client.User(userId)
方法 - 这就是我需要的。但是,SignalR 似乎不知道身份用户 ID。
如果我覆盖 public override Task OnConnectedAsync()
- 内部上下文没有任何有用的信息,例如 user/principals/claims - 是空的。
如何找出哪个 IdentityServer4
用户正在连接到集线器?
EDIT1 建议实施 IUserIdProvider 不起作用 - 所有 xs 均为空。
public string GetUserId(HubConnectionContext connection)
{
var x1 = connection.User?.FindFirst(ClaimTypes.Email)?.Value;
var x2 = connection.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var x3 = connection.User?.FindFirst(ClaimTypes.Name)?.Value;
...
EDIT2 从 https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0 实施了“身份服务器 JWT 身份验证” - 也不起作用 - accessToken
在 PostConfigure
[=20 中为空=]
您需要实施 IUserIdProvider 并将其注册到服务集合中。
检查这个问题 -
据了解,SignalR 服务器上下文中的用户数据为空,因为授权未按我的预期工作。 使用 Identity Server 实现 SignalR 授权似乎是一件大事,并且存在安全风险,因为它会影响整个应用程序 - 您基本上需要手动覆盖 Identity Server 已经完成的大量代码,只是为了满足 SignalR case.
所以我想出了一个 解决方法,请在此处查看我对自己的回答:
编辑:我错过了一个明显的解决方案 - 请参阅其他答案。不过,这仍然是有效的解决方法,所以我打算让它挂在这里。
有一个明显的解决方案。这是在创建具有身份的 asp.net 核心 angular 应用程序后可以使用的示例。
请注意,在这个特定场景中(Angular with ASP.NET Core with Identity)你 NOT 需要实现任何其他,与人们误读文档的多项建议相反:https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0
客户端:
import { AuthorizeService } from '../../api-authorization/authorize.service';
. . .
constructor(. . . , authsrv: AuthorizeService) {
this.hub = new HubConnectionBuilder()
.withUrl("/newshub", { accessTokenFactory: () => authsrv.getAccessToken().toPromise() })
.build();
服务器端:
[Authorize]
public class NewsHub : Hub
{
public static readonly SortedDictionary<string, HubAuthItem> Connected = new SortedDictionary<string, HubAuthItem>();
public override Task OnConnectedAsync()
{
NewsHub.Connected.Add(Context.ConnectionId, new HubAuthItem
{
ConnectionId = Context.ConnectionId,
UserId = Context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value
});
return base.OnConnectedAsync();
}
}
这样使用:
if(NewsHub.Connected.Count != 0)
foreach (var cnn in NewsHub.Connected.Values.Where(i => !string.IsNullOrEmpty(i.UserId)))
if(CanSendMessage(cnn.UserId)
hub.Clients.Users(cnn.UserId).SendAsync("servermessage", "message text");