2

我有一个依赖于服务的 SignalR Core 集线器。该服务本身有它自己的依赖项,其中一个需要访问当前的 ClaimsPrincipal。

我知道,我可以使用 Context.User 属性访问集线器内的 ClaimsPrincipal 并将其作为参数传递给服务,服务也可以将其作为参数传递等等。但我真的不喜欢通过将这种环境信息作为参数传递来污染服务 API。

我尝试使用 IHttpContextAccessor,如下所述:https ://docs.microsoft.com/en-us/aspnet/core/migration/claimsprincipal-current?view=aspnetcore-2.2 这似乎与一个简单的 SignalR 一起工作设置,但它不适用于 Azure SignalR 服务,这将是我们的生产设置。

是否有一种可靠的方法可以在集线器之外获取适用于简单本地设置和 Azure SignalR 服务的 ClaimsPrincipal?

4

1 回答 1

2

在当前的 SignalR 版本 (1.1.0) 中,不支持此功能。我创建了一个功能请求:https ://github.com/dotnet/aspnetcore/issues/18657 ,但被拒绝了。最终,我最终这样做了:

services.AddSingleton(typeof(HubDispatcher<>), typeof(HttpContextSettingHubDispatcher<>));
public class HttpContextSettingHubDispatcher<THub> : DefaultHubDispatcher<THub> where THub : Hub
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpContextSettingHubDispatcher(IServiceScopeFactory serviceScopeFactory, IHubContext<THub> hubContext,
        IOptions<HubOptions<THub>> hubOptions, IOptions<HubOptions> globalHubOptions,
        ILogger<DefaultHubDispatcher<THub>> logger, IHttpContextAccessor httpContextAccessor) :
        base(serviceScopeFactory, hubContext, hubOptions, globalHubOptions, logger)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override async Task OnConnectedAsync(HubConnectionContext connection)
    {
        await InvokeWithContext(connection, () => base.OnConnectedAsync(connection));
    }

    public override async Task OnDisconnectedAsync(HubConnectionContext connection, Exception exception)
    {
        await InvokeWithContext(connection, () => base.OnDisconnectedAsync(connection, exception));
    }

    public override async Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage)
    {
        switch (hubMessage)
        {
            case InvocationMessage _:
            case StreamInvocationMessage _:
                await InvokeWithContext(connection, () => base.DispatchMessageAsync(connection, hubMessage));
                break;
            default:
                await base.DispatchMessageAsync(connection, hubMessage);
                break;
        }
    }

    private async Task InvokeWithContext(HubConnectionContext connection, Func<Task> action)
    {
        var cleanup = false;
        if (_httpContextAccessor.HttpContext == null)
        {
            _httpContextAccessor.HttpContext = connection.GetHttpContext();
            cleanup = true;
        }
        await action();
        if (cleanup)
        {
            _httpContextAccessor.HttpContext = null;
        }
    }
}
于 2020-01-30T09:18:47.377 回答