Authorization in SignalR 2.0

2019-08-10 11:07发布

问题:

I have a Web server with forms authentication and another server that hosts the SignalR Hubs. Using the forms authentication cookie I want to extract the current user using the code below. This would be possible using a HttpModule, but when using SignalR a HttpModule cannot be used.

Is there any other way to archive what I want to do?

public class AuthorizationHubModule : HubPipelineModule
{
    public AuthorizationHubModule()
    {
    }

    protected override bool OnBeforeConnect(IHub hub)
    {
        var cookies = hub.Context.Request.Cookies;
        if (cookies.ContainsKey(".ASPXAUTH") == true)
        {
            // Get the user, populate the Thread.CurrentUser...
            Cookie cookie = cookies[".ASPXAUTH"];

            if (cookie != null)
            {
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

                GenericIdentity identity = new GenericIdentity(ticket.Name);
                GenericPrincipal principal = new GenericPrincipal(identity, new string[0]);

                // Cannot do this because User is readonly (this is possible in a normal HttpModule)
                //hub.Context.User = principal;
            }
        }

        return base.OnBeforeConnect(hub);
    }
}

The thing we are after, is to set the IPrincipal associated with SignalR.

回答1:

If I am not mistaken you are trying to extract user info of the logged in user from context. If that is correct this can be simply achieved by doing the following:

  1. Add [Authorize] attribute to your hub class.
  2. Then use Context.User.Identity to get user identity.

Since you are using persistent connection the authorization process would need to be taken care by you. Refer this link and you can tweak the code provided there as per your need.

To change the identity of the request you must add FormsAuthentication_OnAuthenticate to your global.asax. Here is a sample code that I wrote for my application:

protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e)
    {
        if (FormsAuthentication.CookiesSupported == true)
        {
            if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
            {
                try
                {
                    string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;

                    //Let us set the Pricipal with our user specific details
                    e.User = new System.Security.Principal.GenericPrincipal(
                      new System.Security.Principal.GenericIdentity(username, "Forms"), _user.GetUserRoles(username));                        
                }
                catch (Exception)
                {
                    //somehting went wrong
                }
            }
        }
    }

Hope this helps.



回答2:

Turns out that Signal-R cannot modify the associated IPrincipal because SignalR is not conceived to do authentication, but it gets the authentication context from the application. So the right way of doing this, as @Shashank has pointed out, is to use Forms or whatever you use in your application, and SignalR will get this context from it.



回答3:

Implement the interface IUserIdProvider and add it using

GlobalHost.Configuration.Resolver.Register(typeof(IUserIdProvider), () => provider.Object);

You can read more here.