Signalr 将JWT令牌作为查询字符串传递给信号器集线器

Signalr 将JWT令牌作为查询字符串传递给信号器集线器,signalr,signalr-hub,asp.net-core-signalr,Signalr,Signalr Hub,Asp.net Core Signalr,尝试按照下面链接中的建议将JWT令牌传递给我的信号器集线器,但到目前为止,它不起作用。具体见David Fowler于2017年7月22日提出的建议 我的前端是React,因此我只需将令牌添加到查询字符串中,如下所示,\u令牌具有我的JWT令牌值: const connection = new signalR.HubConnectionBuilder() .withUrl("/myhub?AUTHORIZATION=" + _token) .configureLogging(si

尝试按照下面链接中的建议将
JWT
令牌传递给我的信号器集线器,但到目前为止,它不起作用。具体见David Fowler于2017年7月22日提出的建议

我的前端是
React
,因此我只需将令牌添加到查询字符串中,如下所示,
\u令牌
具有我的
JWT
令牌值:

const connection = new signalR.HubConnectionBuilder()
    .withUrl("/myhub?AUTHORIZATION=" + _token)
    .configureLogging(signalR.LogLevel.Information)
    .build();
在我的
Startup.cs
ConfigureServices()
方法中,我对
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未经授权的
错误

我在这里设置了一个断点,检查该请求是否为web套接字请求,但我没有命中它。看起来管道中有什么东西正在确定用户未经过身份验证


我在代码中做错了什么?

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

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

在旁注中,仅当用户登录时,才应附加令牌:

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

它的作用与另一个答案中提到的相同。它通过从查询字符串中读取令牌,将其添加到头中。当然,您可以在一个单独的文件中分离自定义中间件的实现。

等等,我还需要发布一个来自客户端的代码片段。除了一个区域外,所有内容都非常清楚:看起来在中间件中,您正在查找
头[“Connection”]=“Upgrade”
。这是否意味着您正在信号器连接中设置名为
Connection
的自定义头,其值为
Upgrade
?如果是,您将在哪里添加它?显然,我们正试图将我们在中间件中所做的工作仅限于信号器连接。您是否发现添加此标题是最好的方法?我见过有人在
context.Request
中检查路径,或者在David Fowler的例子中,他在做
context.HttpContext.WebSockets.IsWebSocketRequest
。我想这是一个限制信号器的检查,但是如果你添加
app.UseWebSockets(),我来检查一下我是否能找到关于它的文档
Startup.cs
Configure()
方法中,我们可以使用
context.Websockets.IsWebSocketRequest
来检查请求是否是
signar
的传入请求——假设您不使用
Websockets
任何其他方法。诀窍是在
app.UseAuthentication()
app.usesignator()
之前使用它。我也这么认为。这是我发布的GitHub问题的链接。当我从创造这项技术的团队那里得到一个答案时,我总是感觉好多了:看起来我们正在做一些类似的事情。如果你还有什么问题,就在这里问我。我很乐意help@johnny5非常感谢你。我真的很感激。我还没有看过全部代码,但是我接受了你的解决方案作为答案,所以如果我用一些问题打扰你,请不要生我的气。不用担心,你可以随时打扰我,我会随时提供帮助,我最近刚刚经历了这些挣扎。
        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);
        });