在信号器集线器类上设置 [Authorize] 属性仅在连接到集线器时有效,而不是在集线器上的每个方法之前(ASP.NET Core 2.2)

Posted

技术标签:

【中文标题】在信号器集线器类上设置 [Authorize] 属性仅在连接到集线器时有效,而不是在集线器上的每个方法之前(ASP.NET Core 2.2)【英文标题】:Setting [Authorize] attribute on signalr hub class works only when connecting to the hub and not before each method on the hub (ASP.NET Core 2.2) 【发布时间】:2022-01-16 08:08:41 【问题描述】:

我正在尝试向我的 signalR 集线器添加身份验证,但它仅适用于“协商”方法,而不适用于发送到集线器的每个请求。

Startup.csConfigureService我添加了:

// Authentication
services.AddAuthentication(options =>
        
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        )
        .AddJwtBearer(options =>
        
            options.TokenValidationParameters = GetTokenValidationParameters(authSettings.PublicKey);
            options.IncludeErrorDetails = true;
            options.Events = new JwtBearerEvents
            
                OnMessageReceived = context =>
                
                    var accessToken = context.Request.Query["access_token"];
                    var path = context.HttpContext.Request.Path;

                    if (!string.IsNullOrEmpty(accessToken) &&
                        (path.StartsWithSegments("/hubs")))
                    
                        context.Token = accessToken;
                    
                    return Task.CompletedTask;
                ,
            ;
        );

在我添加的Configure方法中:

app.UseWhen(ctx => ctx.Request.Path.StartsWithSegments("/hubs"), config => config.UseAuthentication());

并在 hub 类上添加了[Authorize] 属性:

[Authorize]
public class ProtocolMethodsHub : Hub<IProtocolMethodsHub>, IProtocolMethodsHub 

我在OnMessageReceived 中设置了一个断点,但它仅在连接到 signalR 时调用,而不是针对通过 websocket 发送的每个请求。

有没有办法对中心的每个请求进行身份验证/授权?

【问题讨论】:

【参考方案1】:

SignalR 在后台使用different transports,每种传输方式的行为可能略有不同。例如,如果它使用 Websocket 传输,您正在服务器和客户端之间创建一个持久连接,并且所有请求都通过此连接(这与普通 HTTP 请求不同),因此它仅在建立期间检查 JWT 有效性的连接。对于其他传输,检查发生得更频繁,但它们仍然仅在您建立与服务器的客户端连接时发生。换句话说,您已经检查了 JWT,并且您知道在请求开始进入时它是有效的,并且 SignalR 不会仔细检查 JWT。如果您想控制 JWT 的授权和访问,您可能需要:

    使用具有不同访问级别的不同集线器。 在 SignalR 集线器中检查令牌的声明,以查看用户是否有权访问他们正在执行的操作。

更新:所以在阅读了 cmets 之后,我意识到您正在寻找当 JWT 过期但连接仍处于活动状态时该怎么办。这只是 Websocket 传输的问题,因为它保持持久连接。我不相信有一种内置的方法来处理这个问题,所以我可以想到两种不同的方法:

    创建 JWT 时,在 JWT 中包含时间戳声明,以便每次 SignalR 集线器中有请求时都可以访问它,如果时间戳过期,只需关闭连接并强制连接创建新 JWT (假设您的客户端中有重新连接逻辑)。使用这种方法,除了时间戳之外,您还可以进行更复杂的检查,以确保 JWT 仍然有效。 在您的客户端代码中有一个计划,每隔几分钟自动关闭和重新打开连接。如果您试图尽量减少连接中断的时间,这可能会导致问题。

【讨论】:

感谢您的详细回复。我们使用 WebSockets,但是我们如何在会话期间处理过期的令牌? 处理实际的 JWT 将取决于您的客户端。如果您使用的是 .NET 客户端,则需要执行以下操作:new HubConnectionBuilder().WithUrl(hubUrl, connectionOptions =&gt; connectionOptions.AccessTokenProvider = // Your access token provider ).Build(); 在客户端上处理 JWT 的检索、存储和生成。 重新阅读您的评论后,我想我可能知道您在问什么。您是否在问当 JWT 过期但连接仍处于活动状态时该怎么办?基本上如何中断连接并要求客户端获取新的JWT? 没错。你知道怎么做吗? @user1977367 我更新了答案,因为它不适合这里。希望对您有所帮助。【参考方案2】:

这样按请求添加/删除中间件感觉不对:

app.UseWhen(ctx => ctx.Request.Path.StartsWithSegments("/hubs"), config => config.UseAuthentication());

您可以在系统的授权部分更好地处理它。 AddJwtBearer 不关心传入请求是否包含令牌。就是这样,如果 thr 请求包含一个令牌,那么它应该是有效的。 AddJwtBearer 确实关心您可以做什么,该工作是授权部分。 AddJwtBearer 想要做的就是根据传入的令牌创建一个 User 对象。

【讨论】:

以上是关于在信号器集线器类上设置 [Authorize] 属性仅在连接到集线器时有效,而不是在集线器上的每个方法之前(ASP.NET Core 2.2)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用信号器允许 cors

Linux基础知识八

SignalR Core 中默认授权所有集线器

断开客户端与服务器端信号器的连接

如何将凭据标志设置为 false?

中继器,集线器,网桥,交换机,路由器