简体   繁体   English

Blazor WebAssembly SignalR 身份验证

[英]Blazor WebAssembly SignalR Authentication

I would love to see an example on how to add authentication to a SignalR hub connection using the WebAssembly flavor of Blazor.我希望看到一个示例,说明如何使用 Blazor 的 WebAssembly 风格向 SignalR 集线器连接添加身份验证。 My dotnet version is 3.1.300.我的 dotnet 版本是 3.1.300。

I can follow these steps to get an open, unauthenticated SignalR connection working: https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr-blazor-webassembly?view=aspnetcore-3.1&tabs=visual-studio我可以按照这些步骤来获得一个开放的、未经身份验证的 SignalR 连接工作: https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr-blazor-webassembly?view=aspnetcore-3.1&tabs=visual工作室

All the tutorials I find seem older or are for a server-hosted type, and don't use the built-in template.我发现的所有教程似乎都较旧或用于服务器托管类型,并且不使用内置模板。

I have added authentication to the rest of the back-end, using the appropriate template and these instructions, including the database: https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view=aspnetcore-3.1我已使用适当的模板和这些说明(包括数据库)向后端的 rest 添加了身份验证: https://docs.microsoft.com/en-us/aspnet/core/security/blazor/?view= aspnetcore-3.1

But every time I add [Authenticate] to the chat hub, I get an error returned.但是每次我将 [Authenticate] 添加到聊天中心时,都会返回一个错误。 Is there any way, extending the first tutorial, that we can authenticate the hub that is created there?有什么方法可以扩展第一个教程,我们可以验证那里创建的集线器? It would be great to hitch on to the built-in ASP.NET system, but I am fine just passing a token in as an additional parameter and doing it myself, if that is best.搭上内置的 ASP.NET 系统会很棒,但是如果最好的话,我可以将令牌作为附加参数传入并自己做。 In that case I would need to learn how to get the token out of the Blazor WebAssembly, and then look it up somewhere on the server.在这种情况下,我需要学习如何从 Blazor WebAssembly 中获取令牌,然后在服务器上的某个位置查找它。 This seems wrong, but it would basically fill my needs, as an alternative.这似乎是错误的,但作为替代方案,它基本上可以满足我的需求。

There are all sorts of half-solutions out there, or designed for an older version, but nothing to build off the stock tutorial that MS presents.那里有各种各样的半解决方案,或者是为旧版本设计的,但没有任何东西可以建立在 MS 提供的库存教程之上。

Update: Following the hints in this news release https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/ , I now can get a token from inside the razor page, and inject it into the header.更新:按照本新闻稿https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-preview-2-release-now-available/中的提示,我现在可以从内部获取令牌razor 页面,并将其注入 header。 I guess this is good??我想这很好?? But then how do I get it and make use of it on the server?但是,我如何获取它并在服务器上使用它呢?

Here is a snippet of the razor code:这是 razor 代码的片段:

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();
    }
}

Update 2: I tried the tip in here: https://github.com/dotnet/aspnetcore/issues/18697更新 2:我在这里尝试了提示: https://github.com/dotnet/aspnetcore/issues/18697

And changed my code to:并将我的代码更改为:

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

But no joy.但没有喜悦。

I've come across the same issue.我遇到了同样的问题。

My solution was 2-sided: I had to fix something in the fronend and in the backend.我的解决方案是双面的:我必须在前端和后端修复一些东西。

Blazor Blazor

In your connection builder you should add the AccessTokenProvider:在您的连接构建器中,您应该添加 AccessTokenProvider:

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

options.AccessTokenProvider is of type Func<Task<string>> , thus you can also perform async operations here. options.AccessTokenProviderFunc<Task<string>>类型,因此您也可以在此处执行异步操作。 Should that be required.如果需要的话。

Doing solely this, should allow SignalR to work.仅这样做,应该允许 SignalR 工作。

Backend后端

However!然而! You might still see an error when SignalR attempts to create a WebSocket connection.当 SignalR 尝试创建 WebSocket 连接时,您可能仍会看到错误。 This is because you are likely using IdentityServer on the backend and this does not support Jwt tokens from query strings.这是因为您可能在后端使用 IdentityServer,而这不支持来自查询字符串的 Jwt 令牌。 Unfortunately SignalR attempts to authorize websocket requests by a query string parameter called access_token .不幸的是 SignalR 试图通过名为access_token的查询字符串参数授权 websocket 请求。

Add this code to your startup:将此代码添加到您的启动中:

.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 : Clarified the usage of the Blazor SignalR code编辑 1 :阐明 Blazor SignalR 代码的用法

This is my solution and works这是我的解决方案并且有效

[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;
        });
    }
}

In my case (Blazor WebAssembly, hosted on ASP.NET Core 5.0 using JWT Bearer Token Auth), I had to add the following:就我而言(Blazor WebAssembly,托管在 ASP.NET Core 5.0 上,使用 JWT Bearer Token Auth),我必须添加以下内容:

Blazor WASM Client Blazor WASM 客户端

When building the connection (in my case: in the constructor of some service proxy class), use IAccessTokenProvider and configure the AccessTokenProvider option like so:建立连接时(在我的例子中:在某些服务代理类的构造函数中),使用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 Core Server ASP.NET核心服务器

Add the following to Startup.ConfigureServices :将以下内容添加到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;
        }
    };
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Blazor Webassembly 认证非常慢 - Blazor Webassembly Authentication is verry slow Blazor WebAssembly 认证及代码含义 - Blazor WebAssembly Authentication and code meaning Blazor Webassembly 中的身份验证和授权最佳实践 - Authentication and Authorisation Best Practice in Blazor Webassembly .NET 5 Blazor 服务器端和 WebAssembly(混合)与 WebAPI 身份验证? - .NET 5 Blazor ServerSide AND WebAssembly (hybrid) with WebAPI Authentication? blazor webassembly windows 身份验证 + .netcore 托管 - blazor webassembly windows authentication + .netcore hosted Blazor 身份验证,用于具有多个 Webassembly 应用程序的托管部署 - Blazor Authentication for Hosted Deployment with Multiple Webassembly Apps Blazor WebAssembly 托管代理在身份验证成功时崩溃 - Blazor WebAssembly Hosted Proxy crash on successful authentication Blazor Webassembly Azure AD 身份验证托管在 Azure 存储帐户中 - Blazor Webassembly Azure AD authentication hosted in Azure storage account 将 Azure Active Directory 身份验证添加到 Blazor WebAssembly 应用程序 - Adding Azure Active Directory authentication to a Blazor WebAssembly app blazor webassembly wasm windows 身份验证/协商+ jwt? api保护 - blazor webassembly wasm windows authentication / negotiate+ jwt? api protection
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM