C# 按域隔离signalR hub中的用户

C# 按域隔离signalR hub中的用户,c#,asp.net,iis,signalr-hub,C#,Asp.net,Iis,Signalr Hub,我有一个web应用程序,它是一个单一的IIS安装(这并没有改变),但是有一个动态的子域集合。每个子域都有自己的用户帐户 我遇到的问题是,当我在其上运行signalR时,它将所有子域视为同一个域,因此碰巧具有相同用户名的用户将相互收到消息 这导致域帐户之间出现安全冲突问题 到目前为止,我的最佳猜测解决方案存在不同程度的风险和问题 每个用户获得自己的组,用子域名+用户名构建组名。 列表项这将碰撞风险降至最低,但不会将其删除 为域名使用Guid,并为Guid保留前n个字符,可以进一步降低风险,但现在,

我有一个web应用程序,它是一个单一的IIS安装(这并没有改变),但是有一个动态的子域集合。每个子域都有自己的用户帐户

我遇到的问题是,当我在其上运行signalR时,它将所有子域视为同一个域,因此碰巧具有相同用户名的用户将相互收到消息

这导致域帐户之间出现安全冲突问题

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

  • 每个用户获得自己的组,用子域名+用户名构建组名。
  • 列表项这将碰撞风险降至最低,但不会将其删除
  • 为域名使用Guid,并为Guid保留前n个字符,可以进一步降低风险,但现在,对于每个在线用户,我都有一个组
  • 在owin启动时,启动一个代表每个域的新中心。
  • 每次添加子域时,我都必须重新启动应用程序以添加新的集线器。现在,我不必做任何事情来添加子域DNS支持通配符,IIS中的主机头为空。除信号器中缺少子域感知外,所有工作正常
  • 与应用程序的其余部分一样,构建一个自定义中心类,使客户机集合域具有感知能力。
  • 这似乎是最干净的,但到目前为止,最耗时。它还带来了bug的最高风险,因为我将不得不在TDD单元测试之外编写更大的QA测试集合
  • 最后一个选项,不要使用信号器,构建我自己的长轮询API。
  • 这是最难接受的,因为它是最高带宽和最公开的进程。对目标用户的一项基本调查显示,他们正在使用支持websocket的浏览器,那么我们为什么要故意增加带宽或创建新的延迟呢

  • 要查看此失败,只需在ASP.NET/SignalR上获取简单的聊天演示,并在本地计算机上的两种不同浏览器(FF和IE用于我的核心测试)下运行它,一个调用http:\localhost,另一个调用http:\yourcomputername。您将需要IIS而不是IIS Express来进行适当的测试。

    我的2美分:构建您自己的
    IUserIdProvider
    实现,从那里可以轻松地检查每个请求并跨多个域生成一个唯一的用户id,您将返回该id,这样信号器就可以知道如何正确地将每个请求关联到谁。这将是一个简单而非侵入性的解决方案。您可以查看更多详细信息。

    我知道这有点晚了,但是我也遇到了这个问题,我已经通过小组解决了这个问题,然而,我的做法是自己实现IHub,然后在调用set
    Clients
    时,在我自己的
    IHubCallerConnectionContext
    实现中包装值,然后使用一个键隔离使用现有方法进行的所有调用。下面是该类的示例:

    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); }
        }
    }
    
    内部类ClientsDatabaseIsolator:IHubCallerConnectionContext
    {
    私有只读字符串\u数据库;
    专用只读IHubCallerConnectionContext\u客户端;
    公共客户端DatabaseIsolator(字符串数据库、IHubCallerConnectionContext客户端)
    {
    如果(database==null)抛出新的ArgumentNullException(nameof(database));
    这个._database=数据库;
    这._clients=客户;
    }
    专用字符串前缀数据库(字符串组)
    {
    返回字符串.Concat(_数据库,“.”,组);
    }
    公共动态AllExcept(参数字符串[]ExcludeConnectionId)
    {
    返回_clients.Group(_数据库,ExcludeConnectionId);
    }
    公共动态客户端(字符串连接ID)
    {
    返回_clients.Client(connectionId);
    }
    公共动态客户端(IList ConnectionId)
    {
    返回_clients.clients(connectionId);
    }
    公共动态组(字符串组名,参数字符串[]ExcludeConnectionId)
    {
    返回_clients.Group(PrefixDatabase(groupName),ExcludeConnectionId);
    }
    公共动态组(IList组名,参数字符串[]ExcludeConnectionId)
    {
    返回_clients.Groups(groupNames.Select(PrefixDatabase.ToList(),excludeConnectionId);
    }
    公共动态用户(字符串用户ID)
    {
    返回_clients.User(userId);
    }
    公共动态用户(IList用户ID)
    {
    返回_clients.Users(userid);
    }
    公共动态所有
    {
    获取{return _clients.Group(_database);}
    }
    公共动态OthersInGroup(字符串groupName)
    {
    返回_clients.OthersInGroup(PrefixDatabase(groupName));
    }
    公共动态其他组(IList组名)
    {
    返回_clients.OthersInGroups(groupNames.Select(PrefixDatabase.ToList());
    }
    公共动态调用者
    {
    获取{return\u clients.Caller;}
    }
    公共动态调用者状态
    {
    获取{return\u clients.CallerState;}
    }
    公共动态其他
    {
    获取{return}clients.OthersInGroup(_数据库);}
    }
    }
    
    然后在
    OnConnected
    中,我将连接添加到
    \u数据库


    现在,在我的中心,当我调用
    Clients.All.Send(“message”)
    时,实际上只会将消息发送到创建
    ClientsDatabaseIsolator
    时指定的组,这就像调用
    Clients.group(database).Send(“message”)
    那样,你就不必考虑它了。我不确定这是否是最好的解决方案,但它对我们有效

    我最终取消了这个项目。显然,服务器上存在的集线器正在扼杀我们的SSRS查看器的性能。虽然我会在有时间的时候重新解释这一点。我最近还意识到,如果使用我的实现,在正常使用组时会出现问题。例如,如果您加入“test”组(如下所示:
    Groups.Add(Context.ConnectionId),则