我正在我的 Blazor 应用程序中构建一个简单的自定义身份验证方法。登录用户必须通过 Razor
<AuthorizeView>
属性才能看到这几个页面。
我可以通过向
ClaimsPrincipal
(通过级联 AuthenticationState
获得)添加一个具有非空身份验证类型的新 ClaimsIdentity
(这是 AuthorizeView
属性在没有 时使用的信息)来使其工作。指定了 Role
或 Policy
,请参阅 为什么我的 ClaimsIdentity IsAuthenticated 始终为 false(对于 Web api 授权过滤器)?)。但是当我想“注销”用户时,我无法删除该“经过身份验证的”ClaimsIdentity。
我找到的解决方法是在主体登录时将名为“loggedin”的角色插入主体(通过新的
ClaimsIdentity
),并在他注销时从相关的ClaimsIdentity
中拔出该角色。控制可见性的 Razor 属性就是 <AuthorizeView Role="loggedin">
。我也可以使用 Policy
,但两者对我来说都显得很脏,因为在这里我只希望页面在用户经过身份验证时可见(而不是“授权”,角色和策略是为这些角色和策略而制定的,我发现这个概念应该与单纯的身份验证区分开来)。但也许这是正确的做法,我不知道。
那么,指定页面仅对经过身份验证的用户可见(没有其他条件)并控制用户的身份验证状态的最简洁方法是什么,仅使用 Razor
AuthorizeView
属性(和子属性),ClaimsPrincipal
和 ClaimsIdentity
?
由于您的问题很少涉及代码,因此这里有一个有关如何执行我认为您想要的操作的演示。这是基于 Blazor 服务器。
它使用自定义的
AuthenticationStateProvider
来捕获原始用户并存储声明。然后,您可以使用捕获的声明构建您自己的 ClaimsIdentity
和 ClaimsPrincipal
。如果您的委托人拥有多个身份,您将需要编写代码来捕获该身份。
自定义身份验证状态提供者:
public class MyAuthenticationProvider : ServerAuthenticationStateProvider
{
ClaimsPrincipal? _user;
IEnumerable<Claim>? _claims;
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var authState = await base.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity?.IsAuthenticated ?? false)
_claims = user.Identities.FirstOrDefault()?.Claims ?? Enumerable.Empty<Claim>();
return authState;
}
public Task LogOutIdentityAsync()
{
_user = new ClaimsPrincipal(new ClaimsIdentity(_claims, null));
var newState = new AuthenticationState(_user);
this.NotifyAuthenticationStateChanged(Task.FromResult<AuthenticationState>(newState));
return Task.CompletedTask;
}
public Task LogInIdentityAsync()
{
_user = new ClaimsPrincipal(new ClaimsIdentity(_claims, "nlm"));
var newState = new AuthenticationState(_user);
this.NotifyAuthenticationStateChanged(Task.FromResult<AuthenticationState>(newState));
return Task.CompletedTask;
}
}
已注册:
// at the end of services definitions
builder.Services.AddScoped<AuthenticationStateProvider, MyAuthenticationProvider>();
用于切换身份验证的演示布局。
@inherits LayoutComponentBase
@inject AuthenticationStateProvider _authProvider
<PageTitle>BlazorApp1</PageTitle>
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4 auth">
<button class="btn btn-primary" @onclick="ToggleAuth">Toggle</button>
<LoginDisplay />
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
@code {
bool _authState = true;
void ToggleAuth()
{
if (_authState)
((MyAuthenticationProvider)_authProvider).LogOutIdentityAsync();
else
((MyAuthenticationProvider)_authProvider).LogInIdentityAsync();
_authState = !_authState;
}
}