如何从 Blazor 服务器端应用程序成功注销?

How to sign out successfully from a Blazor server-side app?

我正在通过执行以下操作注销:

public class LogoutModel : PageModel
{
    public async Task OnGet(string redirectUri)
    {
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
        {
            RedirectUri = redirectUri,
        });
    }
}

注销应该有效,因为我被重定向到正确的页面,在该页面中我有 link 返回主页:

<p class="mb-1">
    @{
        var url = $"{navigationManager.BaseUri}";

        <NavLink class="nav-link" href=@($"{url}") Match="NavLinkMatch.Prefix">
            Login again
        </NavLink>
    }
</p> 

剃须刀页面如下:

<AuthorizeView>
    <Authorized>
        @{
            navigationManager.NavigateTo("Main", forceLoad: true);
        }
    </Authorized>
    <NotAuthorized>
        @{
            var returnUrl = $"{navigationManager.BaseUri}{options.LoginCallbackUrl}";
            navigationManager.NavigateTo($"Login?redirectUri={returnUrl}", forceLoad: true);
        }
    </NotAuthorized>
 </AuthorizeView>

在这个阶段,Authorized 标签内的代码被执行,我立即被重定向到主页。这不应该发生。

Startup.cs中的OpenId设置为:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})

为什么应用程序仍然认为用户仍然被授权?我错过了一步吗?

您必须在主布局上获取身份验证状态 - 生命周期事件 OnInitialized{Async}。

protected override async Task OnInitializedAsync()
{
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
    User = authState.User;
    if (!User.Identity.IsAuthenticated)
    {
        NavMan.NavigateTo("Identity/Account/Login", true);
    }
}

请记住 Blazor 服务器端是 SPA 应用程序,这意味着页面永远不会重新加载,DOM 是通过服务器 <-> SignalR <-> Javascript 隧道更新的。 这意味着 cookie 身份验证只有在您强制重新加载页面时才有可能。

要了解如何处理身份验证(包括注销),请查看此示例。 https://github.com/iso8859/AspNetCoreAuthMultiLang

在AuthenticationStateProvider.GetAuthenticationStateAsync实现中你必须return空令牌

new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); = 未通过身份验证。

不需要子类化任何 类。解决问题的方法是先做:

public class LogoutModel : PageModel
{
    public async Task OnGet(string redirectUri)
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
        {
            RedirectUri = redirectUri,
        });
    }
}

这将从 cookie 和 OpenId 中注销用户。要在此处导航,可以在剃须刀组件中执行以下操作:

@{
    var returnUrl = $"{navigationManager.BaseUri}{LogoutCallbackUrl}";
    <NavLink class="nav-link" href=@($"Logout?redirectUri={returnUrl}") Match="NavLinkMatch.Prefix" />
}

其中 LogoutCallbackUrl 将是重定向的页面,注销已完成。

最后一步是确保注销回调重定向页面允许匿名访问。所以如果回调是一个剃须刀组件,这将通过添加以下内容来完成:

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]