首页 > 解决方案 > SignalR Dotnet 核心 3.1:身份验证 POST /negotiate 返回 200 但 GET /hub 返回 401

问题描述

我有一个 dotnet core 3.1 api,它使用 JWT 不记名令牌进行身份验证,它可以完美地处理 HTTP 请求。SignalR 在没有 [Authorize] 的情况下也能正常工作。我的客户端通过 Authorization 标头发送令牌,因此我认为无需任何额外配置即可进行身份验证。我得到的问题是在初始 POST /hub/negotiate 返回 200 之后 GET /hub 上的 401。

GET  /hub/profile               401 Unauthorized                                
POST /hub/profile/negotiate     200 OK    
Request starting HTTP/1.1 POST https://a8a147ffa245.ngrok.io/hub/profile/negotiate application/x-www-form-urlencoded 0
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[2]
      Successfully validated the token.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
      Authorization was successful.
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
      Executing endpoint '/hub/profile/negotiate'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
      Executed endpoint '/hub/profile/negotiate'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 7.9929ms 200 application/json
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET https://a8a147ffa245.ngrok.io/hub/profile?id=75optzWEnKxa4YZKDnp1Dg  
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      CORS policy execution successful.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
      AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1.7977ms 401

如果我这样做:

options.Events = new JwtBearerEvents
        {
            OnMessageReceived = context =>
            {
                var access_token = context.Request.Headers["Authorization"];

                // If the request is for our hub...
                var path = context.HttpContext.Request.Path;
                
                if (!string.IsNullOrEmpty(access_token) &&
                    (path.StartsWithSegments("/hub")))
                {   
                  Console.WriteLine(access_token);
                }
                return Task.CompletedTask;
            }
        };

我可以看到访问令牌正在正确发送和接收“Bearer {token}”,这与HTTP请求相同。

以下是该应用程序的其他相关方面:

启动.cs

services.AddAuthentication(options =>
      {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      }).AddJwtBearer(options =>
      {
        options.Authority = AUTHORITY;
        options.TokenValidationParameters = new TokenValidationParameters
        {
          ValidateIssuer = true,
          ValidIssuer = ISSUER,
          ValidateAudience = true,
          ValidAudience = AUDIENCE,
          ValidateLifetime = true,
        };
      });

services.AddCors(options =>
      {
        options.AddPolicy("CorsDevPolicy", builder =>
              {
            builder
                      .WithOrigins(new string[]{
                            "https://a8a147ffa245.ngrok.io"
                  })
                      .AllowAnyMethod()
                      .AllowAnyHeader()
                      .AllowCredentials()
                      .SetIsOriginAllowed((host) => true);
          });
      });

      services.AddControllers();
      
      // Signal R
      services.AddSignalR(hubOptions =>
      {
        hubOptions.EnableDetailedErrors = true;
      });

      //CONNECT TO DB
      services.AddScoped<IDbConnection>(x => CreateDbConnection());

}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
        app.UseCors("CorsDevPolicy");
      }
      else
      {
        app.UseHsts();
      }

      app.UseRouting();

      app.UseAuthentication();
      app.UseAuthorization();

      app.UseDefaultFiles();
      app.UseStaticFiles();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapControllers();
        endpoints.MapHub<ProfileHub>("/hub/profile");
      });
    }
  }
}

ProfileHub.cs

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ProfileHub : Hub 
{

    public Task NotifyOrderUpdate(string user, Order orderUpdate)
    {
      return Clients.User(user).SendAsync("NotifyOrderUpdate", orderUpdate);
    }
}

我已经尝试了许多在其他问题、github等上发布的解决方案。我在这里没有看到任何有同样问题的解决方案......

我可能误解了 signalR 的工作原理,因为这是我第一次将它与 Auth 一起使用。为什么有一个初始 POST 到 /negotiate 和一个 GET 之后?

我唯一能想到的可能是令牌在 /negotiate 之后到期?但是令牌在 HTTP 请求中保持有效一段时间。

标签: jwtasp.net-core-signalr

解决方案


推荐阅读