将 JWT 令牌作为查询字符串传递给 SignalR Hub

Passing JWT Token as QueryString to SignalR Hub

尝试按照下面 link 中的建议将 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();

在我的 Startup.csConfigureServices() 方法中,我对 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;
                      }
                  };
              });

这就是我的 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...
   }
}

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

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

我的代码哪里做错了?

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

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

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

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

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

    //...
}

编辑

附带说明一下,您应该只在用户登录后附加令牌:

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

this._hubConnection = new HubConnectionBuilder()
                                .withUrl(hubUrl)
                                .build();

我在我的项目中也实现了这个。执行此操作的最短方法是向您的 Configure 方法添加一个中间件。

        app.Use(async (context, next) =>
        {
            var accessToken = context.Request.Query["access_token"];
            if (!string.IsNullOrEmpty(accessToken))
            {
                context.Request.Headers["authorization"] = "Bearer " + accessToken;
            }

            await next.Invoke().ConfigureAwait(false);
        });

它和另一个答案中提到的一样。它通过从查询字符串中读取它来将标记添加到 header。当然,您可以将自定义中间件的实现单独放在一个单独的文件中。