我有以下 ASP.NET Core (.NET 7) SignalR 测试中心:
public class TestHub : Hub
{
public async Task<string> GetToken()
{
return await Task.FromResult("testtoken");
}
[Authorize]
public async Task<string> Echo(string message)
{
return await Task.FromResult($"Echoing {message}");
}
}
如您所见,我希望 SignalR 客户端从不需要授权的初始
GetToken
调用中检索令牌,然后在连续调用中传递该令牌(使用自定义身份验证方案)以对其进行授权 (Echo
)。
但是,如果我按如下方式在客户端中创建 SignalR 连接,这似乎不可能:
string? token = null;
HubConnection connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5100/test", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.Build();
await connection.StartAsync();
调试显示为
AccessTokenProvider
设置的函数仅在最初启动连接时执行,此时 token
仍然是 null
- 因此任何调用都不会传递令牌。我希望该函数能够针对每个请求运行(从 gRPC 中的类似功能来看)。
这会导致客户端的首次调用成功,因为它不需要授权,但第二次调用失败:
token = await connection.InvokeAsync<string>("GetToken"); // fine
string result = await connection.InvokeAsync<string>("Echo", "Hello?"); // fails
在单个 SignalR 连接中更新令牌的预期方法是什么?
AccessTokenProvider
设置的功能?(我最终还必须在 C++ SignalR 客户端中包含此功能,因此如果有解决方案并且有人知道如何将其添加到 C++ SignalR 客户端中,也非常欢迎此信息。)
在您的场景中,SignalR 客户端中的
AccessTokenProvider
仅在建立连接时调用一次,这就是当时令牌为空的原因。 SignalR 不会像其他一些框架(例如 gRPC)那样自动重新评估每个单独方法调用的 AccessTokenProvider
,并且它希望在启动连接之前设置令牌。
要更新令牌以在后续请求中使用,您可以使用不同的方法手动设置令牌。您可以采取以下几个步骤:
从
GetToken
方法检索令牌后,您可以停止当前连接,然后使用更新的令牌创建新连接。您可以这样做:
// Initially create your connection without a token.
HubConnection connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5100/test", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.Build();
await connection.StartAsync();
// Now when you want to retrieve and set the token.
token = await connection.InvokeAsync<string>("GetToken");
// Create a new connection with the new token
connection = new HubConnectionBuilder()
.WithUrl("http://localhost:5100/test", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.Build();
await connection.StartAsync();
// Now you can call Echo with the updated connection
string result = await connection.InvokeAsync<string>("Echo", "Hello?");
StartAsync
或 Reconnecting
如果您需要更改令牌并且通常必须这样做(例如刷新令牌),请考虑管理连接并仅在拥有有效令牌时启动连接。虽然很乱,但也可以包装在连接管理器类中。
如果使用单独的连接是合适的选择,您可以创建一个专用的集线器来处理令牌获取,另一个用于受保护的调用。这样,您就可以避免在单个连接中管理令牌。
在 C++ SignalR 中,客户端库可能会反映访问令牌管理中的相同限制。您可能还必须通过使用更新的令牌创建新连接或使用专用连接进行身份验证来管理连接。不幸的是,具体的实现细节可能取决于您使用的 C++ 客户端库。
无论选择哪种方法,都必须确保 SignalR 连接的线程安全和正确的生命周期管理,特别是对于生产级应用程序。