.NET Core SignalR: как выполнить авторизацию на основе ресурсов? - PullRequest
0 голосов
/ 12 ноября 2018

Все мои клиенты SignalR подключаются с использованием токена-носителя JWT. Я использую атрибут [Authorize] в моем SignalR Hub.

Этот токен содержит 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);
    }
}

По сути, я хотел бы убедиться, что клиенты, получающие объект Puppy через SignalR, будут авторизованными пользователями на ресурсе. Проблема в том, что Clients - это просто список string идентификаторов клиентов, и я не уверен, как связать их с реальными пользователями на моем ресурсе Puppy.

Как мне добиться этого?

1 Ответ

0 голосов
/ 13 ноября 2018

С самого начала у меня было чувство, что ответ лежит в 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.

...