.Net SignalR 在还配置了 Cookie 身份验证时使用 JWT 承载身份验证

Posted

技术标签:

【中文标题】.Net SignalR 在还配置了 Cookie 身份验证时使用 JWT 承载身份验证【英文标题】:.Net SignalR use JWT Bearer Authentication when Cookie Authentication is also configured 【发布时间】:2021-10-07 15:29:03 【问题描述】:

我有一个 ASP.NET 5 WebApp,它是更大系统的一部分,并使用 Cookie 身份验证来处理浏览器请求。

我想添加在某些 Windows 服务上请求数据和执行特定操作的功能,这些服务也是整个系统的一部分,并在几台单独的 PC 上执行。我想为此使用 SignalR。 然后,Windows 服务作为我们 ActiveDirectory 的一部分的专用服务标识运行。由于服务不应将其用户凭据存储在代码或本地配置文件中,因此它们正在从与 Windows 身份验证一起使用的 API 请求 Web 应用程序的身份验证令牌。

然后,在与 Web 应用建立 SignalR 连接时,服务将使用从 API 接收到的令牌对 Web 应用进行身份验证。这通常是有效的。

web应用的Authentication配置是:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
        
            options.LoginPath = "/Login";
            options.ExpireTimeSpan = TimeSpan.FromMinutes(12);
            options.SlidingExpiration = true;
        )
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
        
            // Configuration details excluded 
            // ...
            opt.Events = new JwtBearerEvents
            
                OnMessageReceived = context =>
                
                    // ...
                
            ;

根据Microsoft Documentation,这应该是一个有效的身份验证配置。

services.AddAuthorization(...) 方法中,我添加了特定于承载方案的策略:

options.AddPolicy("SignalRService", policy =>

    policy.RequireRole("SignalRService"); 
    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
);

然后有一个受此策略保护的 SignalR Hub 方法:

[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString)  /*...*/ 

最后在windows服务中创建hub连接如下:

connection = new HubConnectionBuilder()
    .WithUrl(hubAddress, options =>
    
        options.AccessTokenProvider = () => Task.FromResult(authToken);
    )
    .WithAutomaticReconnect()
    .Build();

建立连接工程:

await connection.StartAsync();

但是当我尝试从 Windows 服务调用集线器方法时,例如 await connection.InvokeAsync("RegisterService", clientId);,我收到带有消息的 HubException

调用“RegisterService”失败,因为用户未经授权

我还在网络应用上创建了一个 API 控制器用于测试目的,并使用相同的策略对其进行保护:

[HttpGet]
[Authorize(Policy = "SignalRService")]
public IActionResult Get()

    return Ok(User.Identity.Name);

当我使用与 SignalR Hub 调用相同的令牌调用此 API 端点时,我得到了按预期返回的令牌上设置的身份。我还验证了配置的 OnMessageReceived 事件处理程序在这种情况下执行,而在我使用 SignalR 连接时没有执行。

当我将JwtBearerDefaults.AuthenticationScheme 设置为Startup.cs 而不是CookieAuthenticationDefaults.AuthenticationScheme 中的默认方案时,它也适用于SignalR Hub,但随后我的标准基于Cookie 的用户身份验证中断。

我希望在调用 Hub 方法时需要一些额外的配置来告诉 Web 应用程序明确使用 Bearer 方案,但到目前为止我找不到任何东西。

【问题讨论】:

【参考方案1】:

又拼命尝试了一个小时,当我将Authorize(Policy = "SignalRService")直接放在类而不是方法上时,我发现特定的承载身份验证使用Cookie身份验证作为默认值。

由于使用 cookie 的浏览器连接也应该可以访问我的集线器,因此我最终得到了:

[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
public class SignalRServiceHub : Hub

    
    [Authorize(Policy = "SignalRService")]
    public async Task RegisterService(string clientIdString)
    
        // ...
    

    [Authorize(Policy = "Root")]
    public async Task RegisterMonitoringClient()
    
        // ...
    

我不完全确定为什么在这种情况下需要在类级别指定方案,而它不适用于 ApiController 实现

【讨论】:

以上是关于.Net SignalR 在还配置了 Cookie 身份验证时使用 JWT 承载身份验证的主要内容,如果未能解决你的问题,请参考以下文章

.net core 3.0 Signalr - 05 使用jwt将用户跟signalr关联

.net core 3.0 Signalr - 05 使用jwt将用户跟signalr关联

ASP.NET Core SignalR实时推送配置,业务层实时推送SignalR消息

ASP.NET Core SignalR:集线器Hubs

SignalR Cors Cookie 未发送

.net core 3.0 Signalr - 08 业务实现-客户端demo