问题:如何管理匿名用户,以便在Hub发出响应时,单个浏览器中的多个标签都会更新?
方案如下:
我想将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上发送命令时如何处理多个标签?
更新 为了清楚起见,我的目的是创建一个实时聊天/支持工具,允许在浏览器中始终进行匿名访问。
客户想要类似于http://www.livechatinc.com
的内容我已经创建了一个javascript插件,可以从集线器发送和接收,无论它在哪个域上。 (我的客户有多站点)这个难题的缺失部分是最简单的,管理匿名用户以允许多标签对话。
答案 0 :(得分:5)
我没有遵循虚假用户帐户的想法(不知道它是否有效),但会开发替代方案。
目标可以通过所有浏览器标签共享的ChatSessionId cookie并创建名为此ChatSessionId的组来实现。
我从asp.net/signalr获取了基本的聊天教程,并添加了允许来自同一用户的多个标签聊天的功能。
1)在" Chat"中分配聊天会话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();
}
}
答案 1 :(得分: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);
}