首页 文章

SignalR C#MVC将匿名用户映射到客户端ID

提问于
浏览
13

Question: How do I manage anonymous users so that multiple tabs in a single browser are all updated when the Hub sends out a response?

方案如下:

我想将SignalR集成到一个项目中,以便匿名用户可以与运营商进行实时聊天 . 显然,使用iIdentity进行身份验证的用户通过Client.User(username)命令进行映射 . 但目前一位匿名用户正在浏览site.com/tools和site.com/notTools我无法仅使用connectionID向所有标签发送消息 . 只有一个选项卡收集响应 .

我尝试过使用IWC补丁,但该工具不考虑将聊天信息保存到数据库中,我认为通过ajax传递变量并不是一种读取/写入数据库的安全方法 .

我还查看了以下内容:Managing SignalR connections for Anonymous user

然而,创建一个这样的基础并使用会话似乎不如Owin安全 .

我有想法通过每次用户连接时创建一个虚假帐户来使用client.user(),并在断开连接时将其删除 . 但我宁愿不用垃圾帐户填充我的aspnetusers数据库上下文似乎没必要 .

是否可以使用 UserHandler.ConnectedIds.Add(Context.ConnectionId); 来映射假用户名?我不知所措 .

使用iUserId提供商是否有意义?

public interface IUserIdProvider
{
    string GetUserId(IRequest request);
}

然后创建一个数据库,将IP地址存储到connectionID和单个用户名?

数据库:

用户:使用FK连接ID

|userid|username|IP       |connectionIDs|
|   1  | user1  |127.0.0.1|  1          |
|   2  | user2  |127.0.0.2|  2          |

connectionIDs:

|connectionID|SignalRID|connectionIDs|
|      1     | xx.x.x.x|     1       |
|      2     | xx.xxx.x|     2       |
|      3     | xx.xxxxx|     2       |
|      4     | xxxxx.xx|     2       |

然后可能在连接周围编写逻辑?

public override Task OnConnected()
    {
     if(Context.Identity.UserName != null){
      //add a connection based on the currently logged in user. 
     }
    else{
     ///save new user to database?
     } 
  }

但问题仍然是如何在websocket上发送命令时如何处理多个标签?

update 为了清楚起见,我的目的是创建一个实时聊天/支持工具,允许在浏览器中始终进行匿名访问 .

客户想要类似http://www.livechatinc.com的东西

我已经创建了一个javascript插件,可以从集线器发送和接收,无论它在哪个域上 . (我的客户有多站点)这个难题的缺失部分是最简单的,管理匿名用户以允许多标签对话 .

2 回答

  • 3

    广泛采用的解决方案是使用户在onConnected上注册带有连接的某种id .

    public override Task OnConnected()
            {
                Clients.Caller.Register();
    
    
                return base.OnConnected();
            }
    

    而且用户通过某种自己的id逻辑调用返回

    来自客户注册方法

    public void Register(Guid userId)
        {
            s_ConnectionCache.Add(userId, Guid.Parse(Context.ConnectionId));
        }
    

    并且将用户ID保存在静态字典中(因为您需要它来保护线程安全;

    static readonly IConnectionCache s_ConnectionCache = new ConnectionsCache();
    

    这里

    public class ConnectionsCache :IConnectionCache
    {
        private readonly Dictionary<Guid, UserConnections> m_UserConnections = new Dictionary<Guid, UserConnections>();
        private readonly Dictionary<Guid,Guid>  m_ConnectionsToUsersMapping = new Dictionary<Guid, Guid>();
        readonly object m_UserLock = new object();
        readonly object m_ConnectionLock = new object();
        #region Public
    
    
        public UserConnections this[Guid index] 
            => 
            m_UserConnections.ContainsKey(index)
            ?m_UserConnections[index]:new UserConnections();
    
        public void Add(Guid userId, Guid connectionId)
        {
            lock (m_UserLock)
            {
                if (m_UserConnections.ContainsKey(userId))
                {
                    if (!m_UserConnections[userId].Contains(connectionId))
                    {
    
                        m_UserConnections[userId].Add(connectionId);
    
                    }
    
                }
                else
                {
                    m_UserConnections.Add(userId, new UserConnections() {connectionId});
                }
            }
    
    
                lock (m_ConnectionLock)
                {
                    if (m_ConnectionsToUsersMapping.ContainsKey(connectionId))
                    {
                        m_ConnectionsToUsersMapping[connectionId] = userId;
                    }
                    else
                    {
                            m_ConnectionsToUsersMapping.Add(connectionId, userId);
                    }
                }
    
        }
    
        public void Remove(Guid connectionId)
        {
            lock (m_ConnectionLock)
            {
                if (!m_ConnectionsToUsersMapping.ContainsKey(connectionId))
                {
                    return;
                }
                var userId = m_ConnectionsToUsersMapping[connectionId];
                m_ConnectionsToUsersMapping.Remove(connectionId);
                m_UserConnections[userId].Remove(connectionId);
            }
    
    
    
        }
    

    一个示例调用注册形成一个Android应用程序

    mChatHub.invoke("Register", PrefUtils.MY_USER_ID).get();
    

    对于JS,它会有点相同

    chat.client.register = function () {
        chat.server.register(SOME_USER_ID);
    }
    
  • 5

    我没有遵循虚假用户帐户的想法(不知道它是否有效),但会开发一种替代方案 .

    目标可以通过所有浏览器选项卡共享的ChatSessionId cookie并创建名为此ChatSessionId的组来实现 .

    我从asp.net/signalr获取了基本的聊天教程,并添加了允许来自同一用户的多个标签聊天的功能 .

    1)在“聊天”操作中分配聊天会话ID以识别用户,因为我们没有用户凭证:

    public ActionResult Chat()
        {
            ChatSessionHelper.SetChatSessionCookie();
            return View();
        }
    

    2)进入聊天页面时订阅聊天会话

    客户端

    $.connection.hub.start()
            .then(function(){chat.server.joinChatSession();})
            .done(function() {
               ...
    

    服务器端(集线器)

    public Task JoinChatSession()
    {
        //get user SessionId to allow use multiple tabs
        var sessionId = ChatSessionHelper.GetChatSessionId();
        if (string.IsNullOrEmpty(sessionId)) throw new InvalidOperationException("No chat session id");
    
        return Groups.Add(Context.ConnectionId, sessionId);
    }
    

    3)向用户的聊天会话广播消息

    public void Send(string message)
    {
        //get user chat session id
        var sessionId = ChatSessionHelper.GetChatSessionId();
        if (string.IsNullOrEmpty(sessionId)) throw new InvalidOperationException("No chat session id");
    
        //now message will appear in all tabs
        Clients.Group(sessionId).addNewMessageToPage(message);
    }
    

    最后,(简单)ChatSessionHelper类

    public static class ChatSessionHelper
    {
        public static void SetChatSessionCookie()
        {
            var context = HttpContext.Current;
            HttpCookie cookie = context.Request.Cookies.Get("Session") ?? new HttpCookie("Session", GenerateChatSessionId());
    
            context.Response.Cookies.Add(cookie);
        }
    
        public static string GetChatSessionId()
        {
            return HttpContext.Current.Request.Cookies.Get("Session")?.Value;
        }
    
        public static string GenerateChatSessionId()
        {
            return Guid.NewGuid().ToString();
        }
    }
    

相关问题