将 JWT 令牌作为 QueryString 传递给 SignalR Hub

Sam*_*Sam 3 signalr signalr-hub asp.net-core-signalr

尝试按照以下链接中的建议将JWT令牌传递到我的 SignalR 集线器,但到目前为止它不起作用。具体参见 David Fowler 在 2017 年 7 月 22 日的建议。https://github.com/aspnet/SignalR/issues/130

我的前端是React这样,我只是将令牌添加到查询字符串中,如下所示,其中_token有我的JWT令牌值:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/myhub?AUTHORIZATION=" + _token)
    .configureLogging(signalR.LogLevel.Information)
    .build();
Run Code Online (Sandbox Code Playgroud)

ConfigureServices()my的方法中Startup.cs,我对Jwt令牌进行了以下配置:

services.AddAuthentication(options => {
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
              .AddJwtBearer(jwtOptions => {
                  jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0/";
                  jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
                  jwtOptions.Events = new JwtBearerEvents
                  {
                      OnMessageReceived = context =>
                      {
                          if(context.HttpContext.WebSockets.IsWebSocketRequest)
                              context.Token = context.Request.Query["AUTHORIZATION"];

                          return Task.CompletedTask;
                      }
                  };
              });
Run Code Online (Sandbox Code Playgroud)

这就是我的Hub样子:

[Authorize]
public class MyHub : Hub
{
   private IBackendService _backendService;
   public MyHub(IBackendService backendService)
   {
       _backendService = backendService;
   }

   public async Task SendMessage(string message)
   {
       // Regular SignalR stuff
       // SignalR will now send the message to all connected users...
   }
}
Run Code Online (Sandbox Code Playgroud)

基本上,我收到了401 Unauthorized错误。

我在断点处检查请求是否是网络套接字请求,但我没有命中它。看起来管道中的某些东西正在确定用户未通过身份验证。

我的代码做错了什么?

joh*_*y 5 5

您可以通过使用自定义中间件处理从查询字符串中获取身份验证令牌来解决此问题。

public class SignalRQueryStringAuthMiddleware
{
    private readonly RequestDelegate _next;

    public SignalRQueryStringAuthMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // Convert incomming qs auth token to a Authorization header so the rest of the chain
    // can authorize the request correctly
    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Headers["Connection"] == "Upgrade" &&
            context.Request.Query.TryGetValue("authToken", out var token))
        {
            context.Request.Headers.Add("Authorization", "Bearer " + token.First());
        }
         await _next.Invoke(context);
    }
}

public static class SignalRQueryStringAuthExtensions
{
    public static IApplicationBuilder UseSignalRQueryStringAuth(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<SignalRQueryStringAuthMiddleware>();
    }
}
Run Code Online (Sandbox Code Playgroud)

这将尝试获取查询字符串值“authToken”,并将设置头部,以便您可以利用您的身份验证中间件。您需要在管道中的身份验证中间件之前调用它,如下所示:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //...

    app.UseSignalRQueryStringAuth();
    app.UseAuthentication();

    //...
}
Run Code Online (Sandbox Code Playgroud)

编辑

在旁注中,如果用户已登录,您应该只附加令牌:

if (accessToken) {
    hubUrl += '?authToken' +'=' + accessToken;
}

this._hubConnection = new HubConnectionBuilder()
                                .withUrl(hubUrl)
                                .build();
Run Code Online (Sandbox Code Playgroud)