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

SignalR C#MVC将匿名用户映射到客户端ID,c#,asp.net-mvc-5,signalr,C#,Asp.net Mvc 5,Signalr,问题:如何管理匿名用户,以便集线器发出响应时,单个浏览器中的多个选项卡都会更新? 情况如下: 我想将SignalR集成到一个项目中,以便匿名用户可以与运营商进行实时聊天。显然,使用iIdentity进行身份验证的用户通过Client.user(username)命令进行映射。但是目前说有匿名用户正在浏览site.com/tools和site.com/notTools,我不能用connectionID向所有选项卡发送消息。只有一个选项卡收集响应 我曾尝试使用IWC补丁,但该工具无法将聊天信息保存到

问题:如何管理匿名用户,以便集线器发出响应时,单个浏览器中的多个选项卡都会更新?

情况如下:

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

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

我还研究了以下方面:

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

我的想法是使用client.user(),每次用户连接时创建一个假帐户,当用户断开连接时将其删除。但我不希望用垃圾帐户填充我的aspnetusers数据库上下文,因为这似乎是不必要的

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

使用iUserId提供者有意义吗

public interface IUserIdProvider
{
    string GetUserId(IRequest request);
}
然后创建一个存储连接ID和单个用户名的IP地址的数据库

数据库:

用户:使用FK到连接ID

|userid|username|IP       |connectionIDs|
|   1  | user1  |127.0.0.1|  1          |
|   2  | user2  |127.0.0.2|  2          |
连接ID:

|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上发送命令时,如何处理多个选项卡

更新 明确地说,我的目的是创建一个实时聊天/支持工具,允许在浏览器中随时进行匿名访问

客户想要类似的东西


我已经创建了一个javascript插件,它可以从集线器发送和接收信息,而不管它在哪个域上。(我的客户有多个站点)谜题中缺少的部分是最简单的,管理匿名用户以允许多选项卡对话

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

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

我从中学习了基本的聊天教程,并添加了允许同一用户从多个选项卡进行聊天的功能

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

    public ActionResult Chat()
    {
        ChatSessionHelper.SetChatSessionCookie();
        return View();
    }
2) 进入聊天页面时订阅聊天会话

客户端

    $.connection.hub.start()
        .then(function(){chat.server.joinChatSession();})
        .done(function() {
           ...
服务器端(集线器)

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();
    }
}

一种被广泛采用的解决方案是让用户在未连接的网络上使用某种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();
这里

对于JS来说也是一样的

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

您希望匿名用户能够从任何打开的浏览器选项卡与同一站点(即site.com)聊天,对吗?@tede24是的,这正是我想要的。您不能用某种SessionId(Guid)设置cookie吗,为该Id创建一个组并在那里订阅所有连接Id?这样你就可以将消息广播到所有选项卡。发生了什么事?你是否尝试过解决方案或找到了其他解决方案?虽然你的答案会起作用,但它不符合我的需要,这是一个安全样式的聊天系统。任何人都可以伪造他们的cookie并访问ss chat是他们不打算使用的。我需要一个解决方案,该解决方案将基于不可伪造的数据管理ID…很容易。@TEDE24此解决方案将适用于一个域,但没有考虑到两个域需要共享cookie。@chris问题中没有说明两个域,示例始终是site.com/xx。无论如何,您可以可以在需要时与适当的cookie共享cookiesettings@chris关于安全性,我无法理解如何从客户端发送和标识,并将其作为组使用(Dan的回答)可能比从服务器生成GUID更安全。虽然Dans answer要求客户端分配ID,但我可以使用他的代码来形成适合我的设计,方法是创建一个可以由WebAPI从我的服务器创建的哈希字符串,因此您的代码特别要求RAZOR中的视图中的帮助器类返回cookie。你的两个代码都可以工作,但需要不同的用例。他的用例非常符合我的需要。一旦我有了一个专门为我工作的代码,我将自己提供这个问题的答案。虽然你的示例在过渡期间可以工作,但它并不能解决我眼前的问题。应用程序已经存在,但我们无法处理conc当前选项卡。这是唯一的问题。我们不想强制用户注册访问我们的网站。当用户连接并启动对话时,数据是收集、姓名、电子邮件、查询说明。当连接到“已注册”对话时,其他未“注册”的选项卡,不获取即时聊天对话。用户有一个昵称-这是您的“分组”令牌-目标不是让用户注册,目标是将他的连接分组在一起,就像您回复客户端一样。All()。您可以使用dynamic Clients=Clients.Clients(userConnections.Select)轻松获取相关连接(x=>x.ToString()).ToList());然后只将数据推送到这些连接。我在android客户端上使用相同的方法,并且我没有任何注册。我正在将您的响应标记为答案。它在过渡期间起作用,我将在
 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);
        }



    }
 mChatHub.invoke("Register", PrefUtils.MY_USER_ID).get();
 chat.client.register = function () {
    chat.server.register(SOME_USER_ID);
}