管理匿名用户的SignalR连接

时间:2014-12-31 02:01:10

标签: c# asp.net-mvc signalr anonymous

我在ASP.Net MVC 5&amp ;;中使用SignalR 2.1.2版。 NServiceBus并有以下要求

有一个注册页面(匿名身份验证),其中SignalR用于发送通知。每个表单提交都会生成一个新的连接ID,需要将其保存在一个集合中,以便我可以向客户端发送响应。 Context.User.Identity.Name为空,因此_connections.Add(name,Context.ConnectionId);不能在此post

中给出的OnConnected()中心事件中使用

登录页面中存在类似的问题。

如果有可能控制ConnectionId,那么我可以克服这种情况,但看起来新版本的SignalR已经摆脱了连接工厂。

我正在使用Redis缓存,因此一个选项就是编写自己的连接管理代码,以便在其中保留这些连接ID。

第二个选项是使用表单身份验证,以便为这些用户分配“匿名角色”,这限制了对匿名视图/控制器的使用,但为用户提供了“名称”,以便Context.User.Identity .Name不为空。有了这个,我可以使用内置的SignalR机制为我管理连接ID。

2 个答案:

答案 0 :(得分:2)

这就是我们在BaseAnonymousController

中所做的
public class BaseAnonymousController : Controller
{
    protected override void OnAuthentication(System.Web.Mvc.Filters.AuthenticationContext filterContext)
    {
        if (filterContext.Controller.GetType().Name == "AccountController" && filterContext.ActionDescriptor.ActionName == "login")
        {
            Guid result;
            if (!string.IsNullOrEmpty(SessionVariables.UserId) && Guid.TryParse(SessionVariables.UserId, out result))
            {
                //Already a anonymous user, so good to go.
            }
            else
            {
                //Seems to be a logged in a user. So, clear the session
                Session.Clear();
            }
        }

        //Perform a false authentication for anonymous users (signup, login, activation etc. views/actions) so that SignalR will have a user name to manage its connections
        if (!string.IsNullOrEmpty(SessionVariables.UserId))
        {
            filterContext.HttpContext.User = new CustomPrincipal(new CustomIdentity(SessionVariables.UserId, "Anonymous"));
        }
        else
        {
            string userName = Guid.NewGuid().ToString();
            filterContext.HttpContext.User = new CustomPrincipal(new CustomIdentity(userName, "Anonymous"));
            FormsAuthentication.SetAuthCookie(userName, false);
            SessionVariables.UserId = userName;
        }

        base.OnAuthentication(filterContext);
    }
}

并将此类用作所有匿名控制器的基类。

public class AccountController : BaseAnonymousController
{
    [AllowAnonymous]
    public ActionResult Signup()
    {
        //Your code
    }

    [AllowAnonymous]
    public ActionResult Login()
    {
        //Your code
    }

    [AllowAnonymous]
    public ActionResult ForgotPassword()
    {
        //Your code
    }

    [AllowAnonymous]
    public ActionResult ForgotUsername()
    {
        //Your code
    }
}

在SignalR中心(没有什么比SignalR文档中的特别之处)

public override Task OnConnected()
    {
        SignalRConnectionStore.Add(Context.User.Identity.Name, Context.ConnectionId);

        return base.OnConnected();
    }

    public override Task OnReconnected()
    {
        string name = Context.User.Identity.Name;
        //Add the connection id if it is not in it 
        if (!SignalRConnectionStore.GetConnections(name).Contains(Context.ConnectionId))
        {
            SignalRConnectionStore.Add(name, Context.ConnectionId);
        }

        return base.OnReconnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        SignalRConnectionStore.Remove(Context.User.Identity.Name, Context.ConnectionId);

        return base.OnDisconnected(stopCalled);
    }

这适用于匿名用户和经过身份验证的用户。

SignalRConnectionStore类和接口

public interface ISignalRConnectionStore
{
    int Count { get; }
    void Add(string userName, string connectionId);
    IEnumerable<string> GetConnections(string userName);
    void Remove(string userName, string connectionId);
}

internal class SignalRConnectionStore : ISignalRConnectionStore
{
    private readonly Dictionary<string, HashSet<string>> _connections = new Dictionary<string, HashSet<string>>();

    public int Count
    {
        get
        {
            return _connections.Count;
        }
    }

    public void Add(string userName, string connectionId)
    {
        if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(connectionId))
        {
            lock (_connections)
            {
                HashSet<string> connections;
                if (!_connections.TryGetValue(userName, out connections))
                {
                    connections = new HashSet<string>();
                    _connections.Add(userName, connections);
                }

                lock (connections)
                {
                    connections.Add(connectionId);
                }
            }
        }
    }

    public IEnumerable<string> GetConnections(string userName)
    {
        if (!string.IsNullOrEmpty(userName))
        {
            HashSet<string> connections;
            if (_connections.TryGetValue(userName, out connections))
            {
                return connections;
            }
        }

        return Enumerable.Empty<string>();
    }

    public void Remove(string userName, string connectionId)
    {
        if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(connectionId))
        {
            lock (_connections)
            {
                HashSet<string> connections;
                if (!_connections.TryGetValue(userName, out connections))
                {
                    return;
                }

                lock (connections)
                {
                    connections.Remove(connectionId);

                    if (connections.Count == 0)
                    {
                        _connections.Remove(userName);
                    }
                }
            }
        }
    }
}

在Hub类中声明SignalRConnectionStore的静态变量,如下所示。

public class ProvisioningHub : Hub
{
    private static ISignalRConnectionStore SignalRConnectionStore;

    public ProvisioningHub(ISignalRConnectionStore signalRConnectionStore)
        : base()
    {
        SignalRConnectionStore = signalRConnectionStore; //Injected using Windsor Castle
    }
}

答案 1 :(得分:0)

使用表单身份验证,存储联合Cookie并将中心区域存储在cookie中。 在SignalR jQuery代码中,使用jQuery插件读取HTTP cookie并获取区域名称并订阅通知。

或者,在.cshtml中,使用从View Model填充的区域渲染jQuery。

注意:使用FormsAuthentication.SetAuthCookie,因为这将创建HTTP Only cookie,并将在Ajax和非Ajax调用中发送。