使用webAPI承载令牌进行SignalR认证

+我使用这个解决方案使用ASP.NET Web API 2,Owin和Identity实现基于令牌的身份validation……效果非常好。 我使用了这个其他的解决方案 ,这通过将承载令牌传递给连接字符串来实现signalR集线器授权和认证,但看起来要么是承载令牌没有,要么某处出现其他错误,这就是为什么我在这里寻求帮助。 ..这些是我的代码… QueryStringBearerAuthorizeAttribute:这是负责validation的类

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; } } } 

这是我的启动课……

 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); } } } 

这是客户端上的淘汰赛加jquery代码….

 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()); 

这是我的中心class……

 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); } } } 

…现在当我尝试调用经过身份validation的集线器类时出现问题,我收到此错误

 caller is not authenticated to invove method sendMessage in impAuthHub 

但后来我在QueryStringBearerAuthorizeAttribute类中更改此方法,总是这样返回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 true; } 

……它的作用……我的代码或实现的问题是什么?

您需要像这样配置您的信号器;

 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); }); 

然后,您需要为signalR编写一个基本的自定义OAuthBearerAuthenticationProvider,它接受access_token作为查询字符串。

 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(null); } } 

在此之后,您只需要发送带有signalr连接的access_token作为查询字符串。

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

而对于你的集线器只是普通的[授权]属性

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

希望这可以帮助。 YD。

无法发表评论,所以在对彼得的优秀答案发表评论后加上我的答案。

我在自定义owin授权提供程序中设置了更多挖掘和用户ID隐藏在此处(显示完整的集线器方法)。

  [Authorize] public async Task 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; } 

为texas697添加了更多 :

Startup.Auth.cs将此添加到ConfigureAuth()(如果尚未添加):

 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); }); 

自定义身份validation提供程序如下所示:

 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(null); } }