我在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。
答案 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调用中发送。