c# - net - signalr methods
Autenticación SignalR con webAPI Portador Token (2)
Necesitas configurar tu señal como esta;
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);
});
Luego, debe escribir un OAuthBearerAuthenticationProvider personalizado básico para signalR que acepte access_token como cadena de consulta.
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);
}
}
Después de esto, todo lo que necesita es enviar access_token con la señal de conexión como una cadena de consulta.
$.connection.hub.qs = { ''access_token'': token };
Y para su hub, solo el atributo [Autorizar] ordinario
public class impAuthHub : Hub
{
[Authorize]
public void SendMessage(string name, string message)
{
Clients.All.newMessage(name, message);
}
}
Espero que esto ayude. YARDA.
+ Utilicé esta solución para implementar la autenticación basada en token utilizando ASP.NET Web API 2, Owin e Identity ... que funcionó de manera excelente. Utilicé esta otra solución para implementar la autorización y autenticación de los hubs de SignalR al pasar el token del portador a través de una cadena de conexión, pero parece que el token del portador no va o algo está mal en alguna parte, por lo que estoy buscando ayuda. .. estos son mis códigos ... QueryStringBearerAuthorizeAttribute: esta es la clase a cargo de la verificación
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;
}
}
}
y esta es mi clase de inicio ....
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);
}
}
}
y este es el código de eliminación directa más jquery en el cliente ...
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());
y aquí está mi clase de centro ...
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);
}
}
}
... ahora el problema surge cuando intento invocar una clase de hub autenticada y me aparece este error
caller is not authenticated to invove method sendMessage in impAuthHub
pero luego cambio este método en la clase QueryStringBearerAuthorizeAttribute para devolver siempre el valor verdadero como este
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;
}
... funciona ... ¿CUÁL ES EL PROBLEMA CON MI CÓDIGO O IMPLEMENTACIÓN?
No puedo comentar, así que agregue mi respuesta después de los comentarios sobre la excelente respuesta de Peter.
Hice un poco más de excavación y la identificación de usuario que había establecido en mi proveedor de autorización owin personalizado se ocultaba aquí (se muestra el método de hub completo).
[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;
}
Más añadido para texas697 :
Startup.Auth.cs agrega esto a ConfigureAuth () si aún no está allí:
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);
});
El proveedor de autenticación personalizado se ve así:
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);
}
}