La autenticación SignalR falló al pasar "Portador" a través de la cadena de consulta
Me gustaría habilitar la autenticación en SignalR mientras el servidor estaba alojado en ASP.NET WebAPI, que estoy usando la autenticación OAuth Bearer y el cliente es AngularJS.
En el lado del cliente, originalmente pasé el token Bearer a través del encabezado HTTP y funciona bien con WebAPI. Pero dado que SignalR JavsScript no admite agregar encabezados HTTP enconnection
(es porque WebSocket no admite la especificación de encabezados HTTP) Necesito pasar el token Bearer a través de la cadena de consulta utilizando el código comoself.connection.qs = { Bearer: 'xxxxxx' };
El problema está en el lado de WebAPI, mi SignalR siempre devolvió 401 sin autorización.
A continuación se muestra lo que hice en el lado de WebAPI.
1, especifiquéOAuthBearerAuthenticationOptions.Provider
aQueryStringEnabledOAuthBearerAuthenticationProvider
, que es una clase que creé heredada deOAuthBearerAuthenticationProvider
que puede recuperar el token de portador de la cadena de consulta. Código de la siguiente manera.
public class QueryStringEnabledOAuthBearerAuthenticationProvider : OAuthBearerAuthenticationProvider { private readonly string _name; public QueryStringEnabledOAuthBearerAuthenticationProvider() : this(OAuthDefaults.AuthenticationType) { } public QueryStringEnabledOAuthBearerAuthenticationProvider(string name) { _name = name; } public override Task RequestToken(OAuthRequestTokenContext context) { // try to read token from base class (header) if possible base.RequestToken(context).Wait(); if (string.IsNullOrWhiteSpace(context.Token)) { // try to read token from query string var token = context.Request.Query.Get(_name); if (!string.IsNullOrWhiteSpace(token)) { context.Token = token; } } return Task.FromResult(null); } }
Y lo registró como a continuación mientras se iniciaba WebAPI.
var options = new OAuthBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AuthenticationType = AuthenticationType, Provider = new QueryStringEnabledOAuthBearerAuthenticationProvider(), AccessTokenFormat = _accessTokenFormat, }; config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); app.UseOAuthBearerAuthentication(options);
2, en la parte SignalR creé un atributo de autorización como se muestra a continuación. Nada cambió solo para usarse para agregar un punto de interrupción.
public class BearerAuthorizeAttribute : AuthorizeAttribute { public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request) { return base.AuthorizeHubConnection(hubDescriptor, request); } public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod) { return base.AuthorizeHubMethodInvocation(hubIncomingInvokerContext, appliesToMethod); } }
Y lo registró cuando WebAPI comenzó también.
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 EnableJavaScriptProxies = false }; // Run the SignalR pipeline. We're not using MapSignalR // since this branch already runs under the "/signalr" // path. map.RunSignalR(hubConfiguration); // Require authentication for all hubs var authorizer = new BearerAuthorizeAttribute(); var module = new AuthorizeModule(authorizer, authorizer); GlobalHost.HubPipeline.AddModule(module); });
Encontré, cuando SignalR conectó miQueryStringEnabledOAuthBearerAuthenticationProvider.RequestToken
fue invocado y recuperó la ficha de portador con éxito. Pero entonces cuando SignalRBearerAuthorizeAttribute.AuthorizeHubConnection
se invocó el parámetrorequest.User
Todavía no está autenticado. Entonces devolvió 401.
¿Alguien puede darme algunas ideas sobre lo que hice mal, gracias.