使用 SignalR 的 JWT 身份验证失败

Posted

技术标签:

【中文标题】使用 SignalR 的 JWT 身份验证失败【英文标题】:JWT Authentication Failed with SignalR 【发布时间】:2019-03-18 17:18:41 【问题描述】:

我有一个 REST API(核心 2.1),它需要支持 SPA,使用 SignalR 提供安静的端点和一些实时交互功能;

Hub/MVC 路由在同一台服务器上运行,该服务器也是 JWT 令牌的提供者。

登录后,客户端会收到一个 JWT 令牌,该令牌放在每个 REST 请求的标头上,否则会收到 401(这与 [Authorize] 属性一起使用)。

在客户端,下面的代码尝试连接到我的 /hub 端点: new HubConnectionBuilder().withUrl(HUB_URL, accessTokenFactory: () => this.getToken() ) 如果我将[Authorize] 放在我的 Hub 类中,我会收到以下错误(未经授权,客户端可以正确发送和收听):

到 'wss://localhost:5001/hub?id=MY_ID&access_token=MY_TOKEN' 的 WebSocket 连接失败:HTTP 身份验证失败;没有可用的有效凭据

服务器记录了失败的身份验证:

(在 AddJwtBearerOptions.Events.OnMessageReceived 上触发 console.log) 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 GET https://localhost:5001/hub? id=MY_ID&access_token=MY_TOKEN 信息:Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 授权失败。 信息:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12] AuthenticationScheme:承载被挑战。 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 0.3658 毫秒内完成 401

与这些请求不同的是,使用 SAME JWT TOKEN 和 REST 请求(使用 [Authorize]),使用 Header: Bearer XXXX 而不是查询字符串,触发 OnTokenValidatedOnAuthenticationFailed 永远不会触发,即使身份验证失败:

(在 AddJwtBearerOptions.Events.OnMessageReceived 上触发 console.log) 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[1] 请求开始 HTTP/1.1 GET https://localhost:5001/api/products application/json (在 AddJwtBearerOptions.Events.OnTokenValidated 上触发了 console.log) 信息:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[2] 成功验证令牌。


在我的“Startup.cs”下方查看

ConfigureServices(IServiceCollection)

services.AddSignalR();
services.AddCors(option => option.AddPolicy("CorsPolicy", p => p.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod().AllowCredentials()));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => 
    options.TokenValidationParameters = new TokenValidationParameters
        ValidateIssuer = true,
        ValidIssuer = Configuration["JWT:Issuer"],
        ValidateLifetime = true,
        ValidateAudience = false,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:SecurityKey"]))
    ;
);

Configure(IApplicationBuilder)

app.UseAuthentication();
app.UseCors("CorsPolicy");
app.UseHttpsRedirection();
app.UseMvc();
app.UseSignalR(routes => 
     routes.MapHub<ApplicationHub>("/hub");
);

【问题讨论】:

【参考方案1】:

您还需要在.AddJwtBearer 部分中添加此块:

// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or 
// Server-Sent Events request comes in.
options.Events = new JwtBearerEvents

    OnMessageReceived = context =>
    
        var accessToken = context.Request.Query["access_token"];

        // If the request is for our hub...
        var path = context.HttpContext.Request.Path;
        if (!string.IsNullOrEmpty(accessToken) &&
            (path.StartsWithSegments("/hub")))
        
            // Read the token out of the query string
            context.Token = accessToken;
        
        return Task.CompletedTask;
    
;

这可以在这里找到the docs。

【讨论】:

你绝对让我开心@marcusturewicz。谢谢!!【参考方案2】:

信息:Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] 授权失败。 信息:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12] AuthenticationScheme:承载被挑战。 信息:Microsoft.AspNetCore.Hosting.Internal.WebHost[2] 请求在 0.3658 毫秒内完成 401

花费 20 小时进行修复。 :( 原因在:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class ChatHub : Hub<IChatClient>

我的服务配置:

services.AddAuthentication(options =>

     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
).AddJwtBearer(options => ...)

更新: JS客户端here的完整工作示例。

【讨论】:

你成就了我的一天!谢谢 你也可以像这样使用它:[Authorize(AuthenticationSchemes = "Bearer")]。同样的结果

以上是关于使用 SignalR 的 JWT 身份验证失败的主要内容,如果未能解决你的问题,请参考以下文章

来自 Angular 的 SignalR Core 中的 JWT 身份验证

带有 JWT 身份验证的 SignalR Core Android 客户端返回 NULL

通过查询字符串传递“承载”时,SignalR 身份验证失败

如何使 SignalR 承载身份验证安全或如何不记录查询字符串

混合 JWT 和 Windows 身份验证。 jwt 失败后弹出凭据

Hyperledger Composer 中的 JWT 身份验证失败