我需要能够与在我的页面之一内运行的 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 内部遇到一些冲突吗?
经过几个小时的修补、调试和搜索,我在这里找到了答案: 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 主题中生成的标记中,并且状态开始持续存在。