Blazor WebAssembly SignalR身份验证

时间:2020-05-28 21:12:58

标签: authentication signalr blazor webassembly

我希望看到一个有关如何使用Blazor的WebAssembly风格向SignalR集线器连接添加身份验证的示例。我的dotnet版本是3.1.300。

我可以按照以下步骤操作,以使开放的未经身份验证的SignalR连接正常工作:https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr-blazor-webassembly?view=aspnetcore-3.1&tabs=visual-studio

我发现的所有教程似乎都比较老,或者是针对服务器托管类型的,并且不使用内置模板。

我已使用适当的模板和这些说明(包括数据库)将身份验证添加到后端的其余部分: https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1

但是,每次我向聊天中心添加[身份验证]时,都会返回错误。通过扩展第一个教程,有什么方法可以验证在此创建的中心吗?最好使用内置的ASP.NET系统,但是最好将令牌作为附加参数传入并自己执行。在这种情况下,我将需要学习如何从Blazor WebAssembly中获取令牌,然后在服务器上的某个位置查找令牌。这似乎是错误的,但基本上可以满足我的需求。

那里有各种各样的半解决方案,或者是为较旧的版本设计的,但是没有什么可以补充MS所提供的库存教程的。

更新: 按照此新闻发布https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/中的提示,我现在可以从剃须刀页面内部获取令牌,并将其注入标头中。我想这很好??但是,如何获得它并在服务器上使用它呢?

这是剃刀代码的片段:

protected override async Task OnInitializedAsync()
{
    var httpClient = new HttpClient();
    httpClient.BaseAddress = new Uri(UriHelper.BaseUri);

    var tokenResult = await AuthenticationService.RequestAccessToken();

    if (tokenResult.TryGetToken(out var token))
    {
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Value}");

        hubConnection = new HubConnectionBuilder()
            .WithUrl(UriHelper.ToAbsoluteUri("/chatHub"), options =>
            {
                options.AccessTokenProvider = () => Task.FromResult(token.Value);
            })
            .Build();
    }
}

更新2: 我在这里尝试了提示:https://github.com/dotnet/aspnetcore/issues/18697

并将我的代码更改为:

        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub?access_token=" + token.Value))
            .Build();

但没有喜悦。

3 个答案:

答案 0 :(得分:1)

我遇到了同样的问题。

我的解决方案是两面的:我必须在前端和后端修复某些东西。

Blazor

在连接构建器中,您应该添加AccessTokenProvider:

string accessToken = "eyYourToken";
connection = new HubConnectionBuilder()
    .WithUrl("https://localhost:5001/hub/chat", options =>
    {
        options.AccessTokenProvider = () => Task.FromResult(token.Value);
    })
    .Build();

options.AccessTokenProvider的类型为Func<Task<string>>,因此您也可以在此处执行异步操作。应该这样。

仅执行此操作,应允许SignalR工作。

后端

但是!!当SignalR尝试创建WebSocket连接时,您可能仍然会看到错误。这是因为您可能在后端使用IdentityServer,但它不支持查询字符串中的Jwt令牌。不幸的是,SignalR尝试通过名为access_token的查询字符串参数来授权Websocket请求。

将此代码添加到您的启动中:

.AddJwtBearer("Bearer", options =>
{
    // other configurations omitted for brevity
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];

            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) &&
                (path.StartsWithSegments("/hubs"))) // Ensure that this path is the same as yours!
            {
                // Read the token out of the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});

edit 1 :阐明了Blazor SignalR代码的用法

答案 1 :(得分:0)

这是我的解决方案,并且有效

[Inject] HttpClient httpClient { get; set; }
[Inject] IAccessTokenProvider tokenProvider { get; set; }
HubConnection hubConnection { get; set; }

(...)

private async Task ConnectToNotificationHub()
{
    string url = httpClient.BaseAddress.ToString() + "notificationhub";

    var tokenResult = await tokenProvider.RequestAccessToken();

    if (tokenResult.TryGetToken(out var token))
    {
        hubConnection = new HubConnectionBuilder().WithUrl(url, options =>
        {
            options.Headers.Add("Authorization", $"Bearer {token.Value}");
        }).Build();


        await hubConnection.StartAsync();

        hubConnection.Closed += async (s) =>
        {
            await hubConnection.StartAsync();
        };

        hubConnection.On<string>("notification", m =>
        {
            string msg = m;
        });
    }
}

答案 2 :(得分:0)

就我而言(Blazor WebAssembly,使用 JWT Bearer Token Auth 托管在 ASP.NET Core 5.0 上),我必须添加以下内容:

Blazor WASM 客户端

在建立连接时(在我的例子中:在某些服务代理类的构造函数中),使用 IAccessTokenProvider 并像这样配置 AccessTokenProvider 选项:

public ServiceProxy(HttpClient httpClient, IAccessTokenProvider tokenProvider) {
    HubConnection = new HubConnectionBuilder()
        .WithUrl(
            new Uri(httpClient.BaseAddress, "/hubs/service"),
            options => {
                options.AccessTokenProvider = async () => {
                    var result = await tokenProvider.RequestAccessToken();
                    if (result.TryGetToken(out var token)) {
                        return token.Value;
                    }
                    else {
                        return string.Empty;
                    }
                };
            })
        .WithAutomaticReconnect() // optional
        .Build();
}

ASP.NET 核心服务器

将以下内容添加到 Startup.ConfigureServices

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => {
    // store user's "name" claim in User.Identity.Name
    options.TokenValidationParameters.NameClaimType = "name";

    // pass JWT bearer token to SignalR connection context
    // (from https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0)
    options.Events = new JwtBearerEvents {
        OnMessageReceived = context => {
            var accessToken = context.Request.Query["access_token"];
            // If the request is for on of our SignalR hubs ...
            if (!string.IsNullOrEmpty(accessToken) &&
                (context.HttpContext.Request.Path.StartsWithSegments("/hubs/service"))) {
                 // Read the token out of the query string
                 context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});