Unable to Identify User Context in SignalR hub decorated with “Authorize” attribute

匿名 (未验证) 提交于 2019-12-03 02:36:02

问题:

Server

SignalR hub within MVC 5 WebApi 2, Security: Bearer token

Client

C# class using HttpWebRequest to retrieve bearer token from WebApi controller /Token endpoint

I used the pattern described here and here to deliver the bearer token to my AuthorizeAttribute sub-class.

When the code within the AuthorizeHubConnection method executes the ticket delivered by the call to "secureDataFormat.Unprotect(token)" is always null. I have confirmed the token is identical on both ends of the communication.

Here is the override method:

public override bool AuthorizeHubConnection(AspNet.SignalR.Hubs.HubDescriptor hubDescriptor, IRequest request) {      var dataProtectionProvider = new DpapiDataProtectionProvider();       var secureDataFormat = new TicketDataFormat(dataProtectionProvider.Create());      var token = request.QueryString.Get("Bearer");      var ticket = secureDataFormat.Unprotect(token);       if (ticket != null && ticket.Identity != null && ticket.Identity.IsAuthenticated)      {        // set the authenticated user principal into environment so that it can be used in the future        request.Environment["server.User"] = new ClaimsPrincipal(ticket.Identity);        return true;       }        return false;  } 

When I run hub without the authorize attribute and set a breakpoint within the "OnConnected" override, the Context.User property is also null.

Any assistance would be greatly appreciated.

Rich

回答1:

Here is my solution, WORK on Azure and local. AngularJS, Web API and SignalR request.Environment["server.User"] this code doesn't work on Azure. First i create my Custom Filter Class.

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]     public class QueryStringBearerAuthorizeAttribute : AuthorizeAttribute     {         public override bool AuthorizeHubConnection(Microsoft.AspNet.SignalR.Hubs.HubDescriptor hubDescriptor, IRequest request)         {             var _Authorization = request.QueryString.Get("Bearer");         if (!string.IsNullOrEmpty(_Authorization))         {             var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(_Authorization);              if (ticket != null && ticket.Identity != null && ticket.Identity.IsAuthenticated)             {                 request.Environment["server.User"] = new ClaimsPrincipal(ticket.Identity);                 return true;             }         }         return false;         }         public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)         {             var connectionId = hubIncomingInvokerContext.Hub.Context.ConnectionId;             var request=hubIncomingInvokerContext.Hub.Context.Request;             var _Authorization = request.QueryString.Get("Bearer");             if (!string.IsNullOrEmpty(_Authorization))             {                 //var token = _Authorization.Replace("Bearer ", "");                 var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(_Authorization);                  if (ticket != null && ticket.Identity != null && ticket.Identity.IsAuthenticated)                 {                     Dictionary<string, object> _DCI = new Dictionary<string, object>();                     _DCI.Add("server.User", new ClaimsPrincipal(ticket.Identity));                     hubIncomingInvokerContext.Hub.Context = new HubCallerContext(new ServerRequest(_DCI), connectionId);                     return true;                 }             }             return false;         } } 

Then in all my connection from SignalR i put

connection.qs = { Bearer: localStorageService.get('authorizationData').token };

My Startup Class

public void Configuration(IAppBuilder app)         {             app.Map("/signalr", map =>             {                 map.UseCors(CorsOptions.AllowAll);                 var hubConfiguration = new HubConfiguration                 {                     EnableDetailedErrors = true                 };                 var authorizer = new QueryStringBearerAuthorizeAttribute();                 var module = new AuthorizeModule(authorizer, authorizer);                 GlobalHost.HubPipeline.AddModule(module);                 map.RunSignalR(hubConfiguration);             });             GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());             ConfigureAuth(app);         } 

It works perfect for me, i'm not sure if sending my token for quesry string instead from header is a security issue. Thats my solution using angularjs, asp.net web api, signal r for autenticate SignalR hubs with a beared token.

In your Hub you can Access to User variable in this way

public ClaimsPrincipal _User { get { return Context.Request.Environment["server.User"] as ClaimsPrincipal; } } 


回答2:

Finally figured this out, I was using the wrong library to decrypt the token. DpapiDataProtectionProvider is used in self-host scenarios, we are hosted in IIS. Here is the functioning code.

 public override bool AuthorizeHubConnection(Microsoft.AspNet.SignalR.Hubs.HubDescriptor    hubDescriptor, IRequest request)  {        var token = request.QueryString.Get("Bearer");        var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(token);          if (ticket != null && ticket.Identity != null && ticket.Identity.IsAuthenticated)         {              // set the authenticated user principal into environment so that it can be used in the future              request.Environment["server.User"] = new ClaimsPrincipal(ticket.Identity);              return true;         }         return false;   } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!