我有一个使用 JWT 不记名令牌进行身份验证的 dotnet 核心 3.1 api,它可以完美地处理 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 请求相同。
以下是该应用程序的其他相关方面:
Startup.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 请求时会保持有效一段时间。