2016-01-21 47 views
12

问:我如何管理匿名用户,这样,当集线器发送一个响应在一个浏览器多标签都被更新?SignalR C#MVC映射匿名用户于客户端ID

的场景如下:

我想SignalR集成到一个项目,这样匿名用户可以生活与运营商聊天。很明显,用iIdentity进行身份验证的用户通过Client.User(用户名)命令进行映射。但是现在说一个匿名用户正在浏览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上发送命令时,我将如何处理多个选项卡?

更新 需要明确的是,我的意图是建立一个即时聊天/支持工具,它允许在任何时候都在浏览器的匿名访问。

客户希望类似http://www.livechatinc.com

东西,我已经创建了发送和从集线器接收,不管它是什么域的JavaScript插件。 (我的客户拥有多站点),这个难题最缺乏的部分是管理匿名用户,以允许进行多标签对话。

+0

你想要什么是匿名用户能够从任何打开的浏览器标签聊天到相同的网站(即site.com),对吧? – tede24

+0

@ tede24是的,这是我想要的。 – Chris

+3

难道你不能只是用某种SessionId(Guid)设置一个cookie,为该Id创建一个组并为其订阅所有的连接ID?这样你可以将消息广播到所有标签 – tede24

回答

2

广泛采用的解决方案是让用户用自己的某种标识与注册连接回来,onConnected。

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); 
} 
+0

尽管您的示例在此期间可以正常工作,但它并不能解决我的即时问题。该应用程序已经存在,但我们无法处理并发标签。这是唯一的问题。我们不希望强制用户注册访问我们的网站。当用户连接并启动会话数据收集时,名称,电子邮件,查询描述。在连接到“注册”对话时,其他没有“注册”的选项卡不会立即进行聊天对话。 – Chris

+0

用户有一个昵称 - 这里是你的“分组”标记 - 目标不是让用户注册,目标是将他的连接分组在一起,就像你回复Clients.All()一样。您可以使用 动态客户端= Clients.Clients(userConnections.Select(x => x.ToString())。ToList()); ,并将数据仅推送到这些连接。 我使用的是android客户端,并且我没有任何注册 –

+0

我正在将您的回应标记为答案。它在此期间有效,我会在稍后的日子继续发展。 – Chris

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

虽然你的答案会起作用,但它不符合我所需要的,这是一个安全风格的聊天系统。任何人都可以伪造他们的cookies并访问他们不打算使用的聊天内容。我需要一个解决方案,可以根据无法伪造的数据管理ID。@ tede24 – Chris

+0

该解决方案适用于一个域,但不考虑两个域需要共享cookie。 – Chris

+0

@chris问题中没有说明两个域,该示例始终为site.com/xx。无论如何,只要你需要正确的cookie设置,你就可以分享cookies – tede24