You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core SignalR AccessTokenProvider未在每次请求执行,如何更新令牌?

问题描述

我有一个ASP.NET Core(.NET 7)SignalR测试Hub:

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)中使用该令牌(自定义认证方案)进行授权。

但按以下方式创建客户端连接时无法实现需求:

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"); // 正常
string result = await connection.InvokeAsync<string>("Echo", "Hello?"); // 失败

请问在单个SignalR连接中更新令牌的预期方式是什么?

  • 是否可以重新执行AccessTokenProvider设置的函数?
  • 是否可以在客户端手动设置令牌?
  • 还是必须创建单独的Hub并使用两个连接,一个仅用于获取令牌,另一个从启动时就需要令牌?

(最终我还需要在C++ SignalR客户端中实现此功能,若有解决方案且了解如何适配C++客户端,相关信息也非常欢迎。)


解决方案

1. 让AccessTokenProvider动态返回最新令牌

不需要重新设置AccessTokenProvider,只要让它返回当前最新的令牌值即可。修改客户端代码,让回调直接引用可更新的变量:

string? token = null;

HubConnection connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:5100/test", options =>
    {
        // 每次需要令牌时都会执行此回调,返回当前最新的token值
        options.AccessTokenProvider = () => Task.FromResult(token);
    })
    .Build();

await connection.StartAsync();

// 获取令牌后更新变量
token = await connection.InvokeAsync<string>("GetToken");

// 此时调用Echo时,AccessTokenProvider会自动返回最新token
string result = await connection.InvokeAsync<string>("Echo", "Hello?"); // 执行成功

注:SignalR的AccessTokenProvider在每次发送需要授权的请求前都会执行回调,之前的问题是初始化时token为null,但更新变量后,后续请求会重新执行回调拿到新值。

2. 手动触发连接协商(可选)

如果需要主动让新令牌立即生效(比如令牌过期刷新场景),可以在更新令牌后调用NegotiateAsync()重新协商连接:

token = "new-refreshed-token";
await connection.NegotiateAsync();

3. C++客户端适配

对于基于cpprestsdk的官方C++ SignalR客户端,可通过http_client_config设置动态令牌提供者:

std::string token;

web::http::client::http_client_config config;
config.set_access_token_provider([&token]() {
    return pplx::task_from_result(token);
});

auto connection = signalr::hub_connection_builder::create("http://localhost:5100/test")
    .with_http_client_config(config)
    .build();

connection.start().wait();

// 获取令牌并更新变量
token = connection.invoke<std::string>("GetToken").get();

// 后续调用Echo时自动使用最新令牌
auto result = connection.invoke<std::string>("Echo", "Hello?").get();

若需手动触发令牌生效,同样调用connection.negotiate().wait()重新协商即可。

是否需要两个连接?

不需要创建两个连接,单个连接完全可以满足需求。只要确保AccessTokenProvider的回调能返回最新令牌值,SignalR会自动在每次授权请求中使用该值。

内容的提问来源于stack exchange,提问作者Ray

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 10:24:53