按域隔离signalR中心的用户

时间:2015-03-21 00:47:12

标签: c# asp.net iis signalr-hub

我有一个单独的IIS安装的Web应用程序(这不会改变),但有一个动态的子域集合。每个子域都有自己的用户帐户。

我遇到的问题是,当我在其上运行signalR时,它会将所有子域视为同一个域,因此恰好具有相同用户名的用户将获得彼此的消息。

这导致我在域帐户之间出现安全违规问题。

到目前为止,我最好的猜测解决方案有不同程度的风险和问题。

  1. 每个用户都有自己的组,使用子域名+用户名构建组名。
    1. 列出项目可以最大限度地降低碰撞风险,但不会将其删除。
    2. 使用Guid作为域名,并为guid保留前n个字符可以进一步降低风险,但现在对于每个在线用户,我现在已经组建了一个组。
  2. 在owin开始时,启动一个代表每个域的新集线器。
    1. 每次添加子域时,我都必须重新启动应用程序才能添加新的集线器。现在,我没有做任何事情来添加DNS支持通配符的子域,并且IIS中的主机头是空白的。除SignalR中缺少子域感知外,所有工作都有效。
  3. 构建一个自定义集线器类,使客户端集合域可以识别,就像应用程序的其余部分一样。
    1. 这似乎是最干净的,但到目前为止,最耗费时间。它也带来了最大的漏洞风险,因为我将不得不在TDD单元测试之外编写更多的QA测试。
  4. 最后一个选项,不要使用SignalR,构建我自己的长轮询API。
    1. 这是最难接受的,因为它是最高带宽和最暴露的过程。对我们的目标用户进行的基本调查表明,他们使用的是支持websocket的浏览器,为什么我们会故意增加带宽或创建新的延迟。
  5. 要查看此失败,只需在ASP.NET/SignalR上获取简单的聊天演示,然后在本地计算机上的两个不同浏览器(FF和IE进行核心测试)下运行它,并调用http:\ localhost另一个叫http:\ yourcomputername。您需要IIS而不是IIS Express才能进行正确的测试。

2 个答案:

答案 0 :(得分:1)

我的2美分:构建自己的IUserIdProvider实现,从那里应该很容易检查每个请求并在多个域中生成一个唯一的用户ID,这样你就会返回,这样SignalR会知道向谁发送正确关联每个请求。它是一种简单而非侵入性的解决方案。您可以查看here了解更多详情。

答案 1 :(得分:1)

我知道这有点晚了,不过我也遇到过这个问题而且我已经使用群组解决了它,但是我这样做是为了自己实现IHub然后设置Clients在我自己的IHubCallerConnectionContext<dynamic>实现中调用值,然后使用键来隔离使用已有方法进行的所有调用。以下是该类的示例:

internal class ClientsDatabaseIsolator : IHubCallerConnectionContext<object>
{
    private readonly string _database;
    private readonly IHubCallerConnectionContext<dynamic> _clients;

    public ClientsDatabaseIsolator(string database, IHubCallerConnectionContext<dynamic> clients)
    {
        if (database == null) throw new ArgumentNullException(nameof(database));
        this._database = database;
        this._clients = clients;
    }

    private string PrefixDatabase(string group)
    {
        return string.Concat(_database, ".", group);
    }

    public dynamic AllExcept(params string[] excludeConnectionIds)
    {
        return _clients.Group(_database, excludeConnectionIds);
    }

    public dynamic Client(string connectionId)
    {
        return _clients.Client(connectionId);
    }

    public dynamic Clients(IList<string> connectionIds)
    {
        return _clients.Clients(connectionIds);
    }

    public dynamic Group(string groupName, params string[] excludeConnectionIds)
    {
        return _clients.Group(PrefixDatabase(groupName), excludeConnectionIds);
    }

    public dynamic Groups(IList<string> groupNames, params string[] excludeConnectionIds)
    {
        return _clients.Groups(groupNames.Select(PrefixDatabase).ToList(), excludeConnectionIds);
    }

    public dynamic User(string userId)
    {
        return _clients.User(userId);
    }

    public dynamic Users(IList<string> userIds)
    {
        return _clients.Users(userIds);
    }

    public dynamic All
    {
        get { return _clients.Group(_database); }
    }

    public dynamic OthersInGroup(string groupName)
    {
        return _clients.OthersInGroup(PrefixDatabase(groupName));
    }

    public dynamic OthersInGroups(IList<string> groupNames)
    {
        return _clients.OthersInGroups(groupNames.Select(PrefixDatabase).ToList());
    }

    public dynamic Caller
    {
        get { return _clients.Caller; }
    }

    public dynamic CallerState
    {
        get { return _clients.CallerState; }
    }

    public dynamic Others
    {
        get { return _clients.OthersInGroup(_database); }
    }
}

然后在OnConnected我将连接添加到_database

现在在我的集线器中,当我致电Clients.All.Send("message")时,它实际上只是在创建ClientsDatabaseIsolator时向指定的群组发送消息,就像打电话给Clients.Group(database).Send("message")一样。我必须考虑一下。我不确定这是否是最佳解决方案,但它对我们有用。