Blazor WASM - 服务器的组件状态丢失

问题描述 投票:0回答:1

我需要能够与在我的页面之一内运行的 blazor wasm 客户端共享来自我的主机应用程序的身份验证令牌。主站点使用 Open Id(是身份服务器)。 我已经阅读了很多这方面的教程,但还没有让它发挥作用。

我想做的主要事情是模仿直接从 Visual Studio 中使用 PersistingServerAuthenticationStateProvider 等的普通 blazor wasm 项目中发生的情况。

据我所知,我已经正确注册了所有内容。当我调试时,我看到我的 PersistingServerAuth 对象被激活然后被释放。当我导航到包含 blazor wasm 的页面时,我希望看到包含这些内容的标记。但不见了。

在 blazor 方面,看起来没有经过身份验证的用户 - 我怀疑是因为身份验证状态未正确共享。

请注意,此设置在 Orchard Core 内部运行。那么也许 OC 正在做一些常规的 ASP.NET Core Web 应用程序会做的事情来中断这种状态?

这是我的 Startup.ConfigureServices 方法。

services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();

services.AddCascadingAuthenticationState();
services.AddScoped<AuthenticationStateProvider, PersistingAuthenticationStateProvider>();

services.AddSignalR();

services.AddHttpsRedirection(options => { options.HttpsPort = 443; });

services.AddOrchardCms()
    .AddSetupFeatures("OrchardCore.AutoSetup")
    .ConfigureServices(services =>
    {
        services.AddAuthorization(options =>
        {
            options.DefaultPolicy = new AuthorizationPolicyBuilder(new[] { 
               JwtBearerDefaults.AuthenticationScheme })
            .RequireAuthenticatedUser()
            .Build();
        });

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.SaveToken = true;
            options.Authority = "https://localhost:4433/";
            options.RequireHttpsMetadata = true;
            options.IncludeErrorDetails = true;
            options.TokenValidationParameters = new 
             Microsoft.IdentityModel.Tokens.TokenValidationParameters()
            {  
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidAudience = "crt_customer_portal",
                ValidIssuer = "https://localhost:4433/",
                ClockSkew = TimeSpan.Zero,
                IssuerSigningKey = new 
                SymmetricSecurityKey(Encoding.UTF8.GetBytes("TODO_REPLACE_TODO_REPLACE")) //TODO Replace with real key
            };
        });
    })
    .Configure((app, routes) =>
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseAntiforgery();
    });

这是我的 blazor wasm 内的启动:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddSingleton<AuthenticationStateProvider, PersistentAuthenticationStateProvider>();   
await builder.Build().RunAsync();

这里是 PersistingAuthenticationStateProvider。它与直接从 Visual Studio 内置的启动 blazor 应用程序几乎相同。

public class PersistingAuthenticationStateProvider : 
ServerAuthenticationStateProvider, IDisposable
{
    private Task<AuthenticationState>? _authenticationStateTask;
    private readonly PersistentComponentState _state;
    private readonly PersistingComponentStateSubscription _subscription;
    private readonly IdentityOptions _options;

    public PersistingAuthenticationStateProvider(PersistentComponentState persistentComponentState, IOptions<IdentityOptions> optionsAccessor)
    {
        _options = optionsAccessor.Value;
        _state = persistentComponentState;
        AuthenticationStateChanged += OnAuthenticationStateChanged;
        _subscription = _state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly);
    }

    private async Task OnPersistingAsync()
    {
        if (_authenticationStateTask is null)
        {
            throw new UnreachableException($"Authentication state not set in {nameof(OnPersistingAsync)}().");
        }

        var authenticationState = await _authenticationStateTask;
        var principal = authenticationState.User;

        if (principal.Identity?.IsAuthenticated == true)
        {
            var tenantId = principal.FindFirst("TenantId")?.Value;
            var userId = principal.FindFirst(_options.ClaimsIdentity.UserIdClaimType)?.Value;
            var name = principal.FindFirst("name")?.Value;
            var bearerToken = principal.FindFirst("bearer")?.Value;

            if (userId != null && name != null && bearerToken != null)
            {
                _state.PersistAsJson(nameof(UserClaims), new UserClaims
                {
                    TenantId = tenantId,
                    UserId = userId,
                    Name = name,
                    BearerToken = bearerToken
                });
            }
        }
    }

    private void OnAuthenticationStateChanged(Task<AuthenticationState> authenticationStateTask)
    {
        _authenticationStateTask = authenticationStateTask;
    }

    public void Dispose()
    {
        _authenticationStateTask?.Dispose();
        AuthenticationStateChanged -= OnAuthenticationStateChanged;
        _subscription.Dispose();
    }
}

有什么想法我可能会错过吗?我可能会在 Orchard Core 内部遇到一些冲突吗?

jwt blazor blazor-webassembly orchardcms
1个回答
0
投票

经过几个小时的修补、调试和搜索,我在这里找到了答案: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/prerender?view=aspnetcore-8.0

“嵌入到页面和视图中的组件(Razor Pages/MVC)”部分有这个小片段。

对于嵌入到 Razor Pages 或 MVC 应用程序的页面或视图中的组件,您必须添加持久组件状态标记帮助程序,并在应用程序布局的结束标记内添加

<persist-component-state />
HTML 标记。仅 Razor Pages 和 MVC 应用程序需要这样做。有关更多信息,请参阅 ASP.NET Core 中的持久组件状态标记帮助程序。

我将其添加到 Orchard Core 主题中生成的标记中,并且状态开始持续存在。

© www.soinside.com 2019 - 2024. All rights reserved.