Domanda SignalR C # MVC Mapping utente anonimo a ID client


Domanda: come gestisco gli utenti anonimi in modo che più schede in un singolo browser vengano tutte aggiornate quando l'hub invia una risposta?

Lo scenario è il seguente:

Mi piacerebbe integrare SignalR in un progetto in modo che gli utenti anonimi possano chattare con gli operatori. Ovviamente gli utenti che si sono autenticati con iIdentity vengono mappati tramite il comando Client.User (username). Al momento, tuttavia, un utente anonimo sta navigando su site.com/tools e site.com/notTools Non riesco a inviare messaggi a tutte le schede con un solo ID di connessione. Solo una scheda raccoglie la risposta.

Ho provato a utilizzare la patch IWC, ma quello strumento non tiene conto del salvataggio delle informazioni della chat in un database e penso che passare le variabili via ajax non sia un modo sicuro di leggere / scrivere su un database.

Ho anche esaminato quanto segue: Gestione delle connessioni SignalR per utente anonimo 

Tuttavia creare una base come quella e usare le sessioni sembra meno sicuro di Owin.

Ho avuto l'idea di usare client.user () creando un account falso ogni volta che un utente si connette e lo cancella quando si disconnettono. Ma preferirei non riempire il mio contesto di db aspnetusers con account spazzatura che sembra non necessario.

È possibile usare UserHandler.ConnectedIds.Add(Context.ConnectionId); anche per mappare un nome utente falso? Sono in perdita.

Avrebbe senso usare il provider iUserId?

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

Quindi creare un database che memorizza gli indirizzi IP in ID di connessione e singoli nomi utente?

Banca dati:

Utenti: con FK per gli ID di connessione

|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       |

Quindi è possibile scrivere la logica intorno alla connessione?

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?
     } 
  }

ma la domanda rimane ancora come gestirò più schede con quella quando invio un comando sul websocket?

aggiornare Per essere chiari, il mio intento è quello di creare uno strumento di chat / supporto live che consenta in qualsiasi momento l'accesso anonimo al browser.

Il cliente vuole qualcosa di simile a http://www.livechatinc.com

Ho già creato un plugin javascript che invia e riceve dall'hub, indipendentemente dal dominio su cui si trova. (il mio cliente ha multisiti) il pezzo mancante del puzzle è il più semplice, gestendo gli utenti anonimi per consentire conversazioni multi-tab.


12
2018-01-21 01:45


origine


risposte:


una soluzione largamente adottata è quella di fare in modo che l'utente si registri con il suo qualche tipo di id con la connessione back, sul onConnected.

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


            return base.OnConnected();
        }

e che l'utente ritorna con una chiamata con qualche tipo di logica id propria

dal metodo di registrazione dei clienti

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

e mantieni gli id ​​utente in un dizionario statico (prenditi cura dei lucchetti poiché hai bisogno che sia sicuro;

static readonly IConnectionCache s_ConnectionCache = new ConnectionsCache();

Qui

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



    }

un esempio di chiamata a Registrati forma un'app per Android

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

per JS sarebbe stato lo stesso

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

2
2018-02-01 22:56



Non sto seguendo l'idea di un falso account utente (non so se funziona), ma svilupperò un'alternativa.

L'obiettivo potrebbe essere raggiunto tramite un cookie ChatSessionId condiviso da tutte le schede del browser e creando gruppi denominati come ChatSessionId.

Ho preso il tutorial di base di chat da asp.net/signalr e ha aggiunto funzionalità per consentire la chat da più schede come lo stesso utente.

1) Assegna un ID sessione chat in azione "Chat" per identificare l'utente, poiché non abbiamo credenziali utente:

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

2) Iscriviti alla sessione di chat quando entri nella pagina di chat

dalla parte del cliente

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

lato server (hub)

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) trasmettere messaggi alla sessione di chat dell'utente

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

Infine, la (semplice) classe 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();
    }
}

5
2018-01-27 19:35