2014-10-30 11 views
11

+ ich habe this solution verwendet, um Token-basierte Authentifizierung mit ASP.NET Web API 2, Owin und Identity zu implementieren ... was ausgezeichnet funktioniert hat. Ich benutzte diese other solution und dies zu SignalR Hubs Autorisierung und Authentifizierung implementieren, indem das Bearer-Token durch eine Verbindungszeichenfolge übergeben, aber scheint entweder das Bearer-Token nicht geht, oder etwas anderes ist falsch irgendwo, weshalb ich hier suche HILFE .. .these meine Codes sind ... QueryStringBearerAuthorizeAttribute: das ist die Klasse mit der Überprüfung betrautenSignalR-Authentifizierung mit webAPI Bearer Token

using ImpAuth.Entities; 
using Microsoft.AspNet.Identity.EntityFramework; 
using Microsoft.Owin.Security; 
using Microsoft.Owin.Security.OAuth; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Security.Claims; 
using System.Threading.Tasks; 
using System.Web; 

namespace ImpAuth.Providers 
{ 
    using System.Security.Claims; 
    using Microsoft.AspNet.SignalR; 
    using Microsoft.AspNet.SignalR.Hubs; 
    using Microsoft.AspNet.SignalR.Owin; 

    public class QueryStringBearerAuthorizeAttribute : AuthorizeAttribute 
    { 
     public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request) 
     { 
      var token = request.QueryString.Get("Bearer"); 
      var authenticationTicket = Startup.AuthServerOptions.AccessTokenFormat.Unprotect(token); 

      if (authenticationTicket == null || authenticationTicket.Identity == null || !authenticationTicket.Identity.IsAuthenticated) 
      { 
       return false; 
      } 

      request.Environment["server.User"] = new ClaimsPrincipal(authenticationTicket.Identity); 
      request.Environment["server.Username"] = authenticationTicket.Identity.Name; 
      request.GetHttpContext().User = new ClaimsPrincipal(authenticationTicket.Identity); 
      return true; 
     } 

     public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod) 
     { 
      var connectionId = hubIncomingInvokerContext.Hub.Context.ConnectionId; 

      // check the authenticated user principal from environment 
      var environment = hubIncomingInvokerContext.Hub.Context.Request.Environment; 
      var principal = environment["server.User"] as ClaimsPrincipal; 

      if (principal != null && principal.Identity != null && principal.Identity.IsAuthenticated) 
      { 
       // create a new HubCallerContext instance with the principal generated from token 
       // and replace the current context so that in hubs we can retrieve current user identity 
       hubIncomingInvokerContext.Hub.Context = new HubCallerContext(new ServerRequest(environment), connectionId); 

       return true; 
      } 

      return false; 
     } 
    } 
} 

und dies ist mein Startklasse ....

using ImpAuth.Providers; 
using Microsoft.AspNet.SignalR; 
using Microsoft.Owin; 
using Microsoft.Owin.Cors; 
using Microsoft.Owin.Security.Facebook; 
using Microsoft.Owin.Security.Google; 
//using Microsoft.Owin.Security.Facebook; 
//using Microsoft.Owin.Security.Google; 
using Microsoft.Owin.Security.OAuth; 
using Owin; 
using System; 
using System.Collections.Generic; 
using System.Data.Entity; 
using System.Linq; 
using System.Web; 
using System.Web.Http; 

[assembly: OwinStartup(typeof(ImpAuth.Startup))] 

namespace ImpAuth 
{ 
    public class Startup 
    { 
     public static OAuthAuthorizationServerOptions AuthServerOptions; 

     static Startup() 
     { 
      AuthServerOptions = new OAuthAuthorizationServerOptions 
      { 
       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/token"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30), 
       Provider = new SimpleAuthorizationServerProvider() 
       // RefreshTokenProvider = new SimpleRefreshTokenProvider() 
      }; 
     } 

     public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; } 
     public static GoogleOAuth2AuthenticationOptions googleAuthOptions { get; private set; } 
     public static FacebookAuthenticationOptions facebookAuthOptions { get; private set; } 

     public void Configuration(IAppBuilder app) 
     { 
      //app.MapSignalR(); 
      ConfigureOAuth(app); 
      app.Map("/signalr", map => 
      { 
       // Setup the CORS middleware to run before SignalR. 
       // By default this will allow all origins. You can 
       // configure the set of origins and/or http verbs by 
       // providing a cors options with a different policy. 
       map.UseCors(CorsOptions.AllowAll); 
       var hubConfiguration = new HubConfiguration 
       { 
        // You can enable JSONP by uncommenting line below. 
        // JSONP requests are insecure but some older browsers (and some 
        // versions of IE) require JSONP to work cross domain 
        //EnableJSONP = true 
        EnableDetailedErrors = true 
       }; 
       // Run the SignalR pipeline. We're not using MapSignalR 
       // since this branch already runs under the "/signalr" 
       // path. 
       map.RunSignalR(hubConfiguration); 
      }); 
      HttpConfiguration config = new HttpConfiguration(); 
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 
      WebApiConfig.Register(config); 
      app.UseWebApi(config); 
     } 

     public void ConfigureOAuth(IAppBuilder app) 
     { 
      //use a cookie to temporarily store information about a user logging in with a third party login provider 
      app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie); 
      OAuthBearerOptions = new OAuthBearerAuthenticationOptions(); 

      OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() 
      { 
       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/token"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
       Provider = new SimpleAuthorizationServerProvider() 
      }; 

      // Token Generation 
      app.UseOAuthAuthorizationServer(OAuthServerOptions); 
      app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 

      //Configure Google External Login 
      googleAuthOptions = new GoogleOAuth2AuthenticationOptions() 
      { 
       ClientId = "1062903283154-94kdm6orqj8epcq3ilp4ep2liv96c5mn.apps.googleusercontent.com", 
       ClientSecret = "rv5mJUz0epWXmvWUAQJSpP85", 
       Provider = new GoogleAuthProvider() 
      }; 
      app.UseGoogleAuthentication(googleAuthOptions); 

      //Configure Facebook External Login 
      facebookAuthOptions = new FacebookAuthenticationOptions() 
      { 
       AppId = "CHARLIE", 
       AppSecret = "xxxxxx", 
       Provider = new FacebookAuthProvider() 
      }; 
      app.UseFacebookAuthentication(facebookAuthOptions); 
     } 
    } 

} 

und dies ist der Knockout-Plus jquery code auf dem Client ....

function chat(name, message) { 
    self.Name = ko.observable(name); 
    self.Message = ko.observable(message); 
} 

function viewModel() { 
    var self = this; 
    self.chatMessages = ko.observableArray(); 

    self.sendMessage = function() { 
     if (!$('#message').val() == '' && !$('#name').val() == '') { 
      $.connection.hub.qs = { Bearer: "yyCH391w-CkSVMv7ieH2quEihDUOpWymxI12Vh7gtnZJpWRRkajQGZhrU5DnEVkOy-hpLJ4MyhZnrB_EMhM0FjrLx5bjmikhl6EeyjpMlwkRDM2lfgKMF4e82UaUg1ZFc7JFAt4dFvHRshX9ay0ziCnuwGLvvYhiriew2v-F7d0bC18q5oqwZCmSogg2Osr63gAAX1oo9zOjx5pe2ClFHTlr7GlceM6CTR0jz2mYjSI" }; 
      $.connection.hub.start().done(function() { 
       $.connection.hub.qs = { Bearer: "yyCH391w-CkSVMv7ieH2quEihDUOpWymxI12Vh7gtnZJpWRRkajQGZhrU5DnEVkOy-hpLJ4MyhZnrB_EMhM0FjrLx5bjmikhl6EeyjpMlwkRDM2lfgKMF4e82UaUg1ZFc7JFAt4dFvHRshX9ay0ziCnuwGLvvYhiriew2v-F7d0bC18q5oqwZCmSogg2Osr63gAAX1oo9zOjx5pe2ClFHTlr7GlceM6CTR0jz2mYjSI" }; 
       $.connection.impAuthHub.server.sendMessage($('#name').val(), $('#message').val()) 
          .done(function() { $('#message').val(''); $('#name').val(''); }) 
          .fail(function (e) { alert(e) }); 
      }); 
     } 
    } 

    $.connection.impAuthHub.client.newMessage = function (NAME, MESSAGE) { 
     //alert(ko.toJSON(NAME, MESSAGE)); 
     var chat1 = new chat(NAME, MESSAGE); 
     self.chatMessages.push(chat1); 
    } 

} 

ko.applyBindings(new viewModel()); 

und hier ist meine Nabe Klasse ...

using ImpAuth.Providers; 
using Microsoft.AspNet.SignalR; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 

namespace ImpAuth 
{ 
    public class impAuthHub : Hub 
    { 
     [QueryStringBearerAuthorize] 
     public void SendMessage(string name, string message) 
     { 

      Clients.All.newMessage(name, message); 
     } 
    } 
} 

... jetzt kommt das Problem, wenn ich versuche, eine authentifizierte Hub Klasse aufzurufen, und ich erhalte diese Störung

caller is not authenticated to invove method sendMessage in impAuthHub 

aber dann ändere ich diese Methode in der QueryStringBearerAuthorizeAttribute-Klasse, um immer so zurück zu kommen.

... es funktioniert .... WAS IST DAS PROBLEM MIT MEINEM CODE ODER IMPLEMENTIERUNG?

+0

ich eine E-Mail Louis geschickt haben, die meine Repo gegabelt und implementiert, um die Integration mit SignalR hoffentlich wird er prüfen und helfen können. Froh, dass meine Beiträge in Ihrem Fall nützlich war :) –

+0

Hallo, danke Taiise für die E-Mail. McKabue, es gibt ein paar Dinge, die ich mir vorstellen kann. Erstens, könnten Sie möglicherweise Ihre Anwendung debuggen und es auf der Linie brechen, wo wir var Prinzipal setzen. Ich würde gerne sehen, welche Werte im Prinzip vergeben werden. Dies wäre der beste Ausgangspunkt. –

+0

+ Hallo Lewis ... ich bekomme einen Null-Prinzip-Wert ... entweder wird der Bearer-Token nicht gesendet, kaufe den Weg, wie überprüfe ich, ob der Bearer-Token vom Client gesendet wird? – McKabue

Antwort

24

Sie müssen Ihren Signalgeber so konfigurieren;

app.Map("/signalr", map => 
{ 
    map.UseCors(CorsOptions.AllowAll); 

    map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions() 
    { 
     Provider = new QueryStringOAuthBearerProvider() 
    }); 

    var hubConfiguration = new HubConfiguration 
    { 
     Resolver = GlobalHost.DependencyResolver, 
    }; 
    map.RunSignalR(hubConfiguration); 
}); 

Dann benötigen Sie einen grundlegenden individuellen OAuthBearerAuthenticationProvider für signalR zu schreiben, die als Query-String access_token akzeptiert.

public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider 
{ 
    public override Task RequestToken(OAuthRequestTokenContext context) 
    { 
     var value = context.Request.Query.Get("access_token"); 

     if (!string.IsNullOrEmpty(value)) 
     { 
      context.Token = value; 
     } 

     return Task.FromResult<object>(null); 
    } 
} 

Danach müssen Sie nur access_token mit Signalverbindung als Querystring senden.

$.connection.hub.qs = { 'access_token': token }; 

Und für Ihre Nabe nur gewöhnliche [autorisieren] Attribut

public class impAuthHub : Hub 
{ 
    [Authorize] 
    public void SendMessage(string name, string message) 
    { 
     Clients.All.newMessage(name, message); 
    } 
} 

Hoffnung, das hilft. YD.

+0

Und wie würden Sie vom Hub "Context" auf den Benutzer zugreifen? Können Sie 'Context.User.Identity.Name' aufrufen? Was genau macht diese Linie? 'context.Token = Wert;' Wie füllt es den Hub-Kontext? Danke! –

+4

Ich habe gerade versucht, diese Methode, aber leider meine, 'Context.User.Identity.Name' ist' null' –

+0

Wo passt der 'Kontext' aus dem' Provider' in die OWIN-Pipeline? Wie wird die Tatsache, dass ich 'context.Token' auf das vom Client abgerufene Token gesetzt habe, meinen' Context' beeinflussen? Jedes Repo/komplexere Beispiel wäre jetzt Gold für mich. Vielen Dank! –

1

Kann nicht kommentieren so meine Antwort nach den Kommentaren auf Peters ausgezeichnete Antwort hinzufügen.

Habe ein bisschen mehr graben und die Benutzer-ID, die ich in meinem benutzerdefinierten Owin Authorization Provider eingestellt hatte, versteckte sich hier (vollständige Hub-Methode gezeigt).

[Authorize] 
    public async Task<int> Test() 
    { 
     var claims = (Context.User.Identity as System.Security.Claims.ClaimsIdentity).Claims.FirstOrDefault(); 
     if (claims != null) 
     { 
      var userId = claims.Value; 

      //security party! 
      return 1; 
     } 

     return 0; 
    } 

Mehr hinzugefügt für texas697:

Startup.Auth.cs fügen Sie diese zu ConfigureAuth(), wenn nicht bereits vorhanden:

app.Map("/signalr", map => 
    { 
     map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions() 
     { 
      Provider = new QueryStringOAuthBearerProvider() //important bit! 
     }); 

     var hubConfiguration = new HubConfiguration 
     { 
      EnableDetailedErrors = true, 
      Resolver = GlobalHost.DependencyResolver, 
     }; 
     map.RunSignalR(hubConfiguration); 
    }); 

Die benutzerdefinierte Auth-Provider wie folgt aussieht:

public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider 
{ 
    public override Task RequestToken(OAuthRequestTokenContext context) 
    { 
     var value = context.Request.Query.Get("access_token"); 

     if (!string.IsNullOrEmpty(value)) 
     { 
      context.Token = value; 
     } 

     return Task.FromResult<object>(null); 
    } 
} 
+0

Hat das funktioniert? Ich erhalte Fehler: Anrufer ist nicht berechtigt, die Methode auf . aufrufen Ich kann sehen, dass das Anfrage-Token korrekt ausgeführt wird und das Token mit dem Recht zugewiesen ist Wert. –

Verwandte Themen