我的所有SignalR客户端都使用JWT承载令牌进行连接。我使用SignalR Hub
中的[Authorize]
属性。
此令牌包含一个userId
,可用于检查用户是否通过资源的users
属性对资源进行了读取访问,该属性包含一个如下所示的List<PuppyUserPermission>
:
public class PuppyUserPermission
{
public string userId { get; set; }
public bool read { get; set; }
public bool write { get; set; }
}
问题是:我如何将这里的点连接起来?理想情况下,而不是像这样的东西
[Authorize]
public class PuppyHub : Hub
{
public async Task SendPuppy(Puppy pup)
{
await Clients.All.SendAsync(pup);
}
}
我想这样做(这是比其他任何东西都多的伪代码,因为我没有使用有效的方法(:
[Authorize]
public class PuppyHub : Hub
{
public async Task SendPuppy(Puppy pup)
{
var clients = Puppy.users.Where(u => u.read == true);
await clients.SendAsync(pup);
}
}
基本上,我想确保通过SignalR接收Puppy
对象的客户端将是资源上的授权用户。问题是,Clients
只是string
客户端ID的列表,我不知道如何将它们绑定到我的Puppy
资源上的实际用户。
我该如何实现这一目标?
从一开始,我就觉得答案在IUserIdProvider
中,但我不知道这对多个用户来说是如何工作的。
我终于找到了答案,但肯定需要一些清理。
首先,创建自己的IUserIdProvider
实现,如下所示:
public class MyUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
var username = connection.User.Claims.Where(x => x.Type == "THE_CLAIM_YOU_WANT_TO_USE_TO_IDENTIFY_USERS").First().Value;
return username;
}
}
接下来,使用DI:进行注册
services.AddSingleton<IUserIdProvider, MyUserIdProvider >();
现在,当你想从服务器发送事件时,在你的构造函数中使用DI像往常一样下拉SignalR Hub
的一个实例:
private IHubContext<PuppyHub> puppyHub { get; }
public UsersController(IHubContext<PuppyHub> _puppyHub)
{
puppyHub = _puppyHub;
}
然后,当你想告诉你的客户关于新的Puppy
:
// ... typical controller code
// assume we have a var, puppy, with a list of authorized users
// use System.Linq to get a list of userIds where the user is authorized to read the puppy
var authorizedUsers = (IReadOnlyList<string>)puppy.users.Where(x => x.permissions.read == true).Select(i => i._id).ToList();
// send the new puppy to the authorized users
await puppyHub.Clients.Users(authorizedUsers).SendAsync("SendPuppy", puppy);
还有中提琴!您现在已经使用SignalR完成了基于资源的授权。