You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET7中Blazor客户端与Duende SSO登出后无法自动跳转登录页问题

问题:SSO登出后Blazor客户端无法自动跳转登录页

我有一个Blazor客户端和使用Duende Identity Server的SSO服务器,当前项目基于.NET7。访问Blazor应用链接https://localhost:7244时会自动重定向到SSO登录,此功能正常。但在SSO端登出后,Blazor客户端的Cookie已被清除,SSO端显示登出完成,但Blazor客户端不会自动重定向到登录页,必须手动刷新页面(F5)才会生效。需要实现SSO登出后Blazor客户端自动跳转到登录页。


SSO端program.cs配置

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<BeaconSSOContext>();

builder.Services.AddIdentityServer()
                .AddInMemoryClients(new Client[] {
                    new Client
                    {
                        ClientId = "client",
                        AllowedGrantTypes = GrantTypes.Implicit,
                        RedirectUris = { "https://localhost:7244/signin-oidc" },
                        PostLogoutRedirectUris = { "https://localhost:7244/signout-callback-oidc" },
                        FrontChannelLogoutUri = "https://localhost:7244/signout-oidc",
                        AllowedScopes = { "openid", "profile", "email", "phone" }
                    }
                })
                .AddInMemoryIdentityResources(new IdentityResource[] {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                    new IdentityResources.Email(),
                    new IdentityResources.Phone(),
                })
                .AddAspNetIdentity<IdentityUser>();

Blazor客户端program.cs配置

// Add services to the container.
builder.Services.AddRazorPages();

builder.Services.AddServerSideBlazor();

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://localhost:7001";
    options.ClientId = "client";
    options.MapInboundClaims = false;
    options.SaveTokens = true;
});

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

builder.Services.AddAntDesign();

ConfigurationHelper.Initialize(builder.Configuration);

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();

app.UseAuthorization();

app.MapBlazorHub();

app.MapFallbackToPage("/_Host");

app.Run();

已尝试的解决方案

  • 向_import.razor添加@attribute [Authorize]
  • 在Razor页面使用@attribute [Authorize]
  • 在MainLayout.razor中使用AuthorizeView和Authorized组件

可行解决方案

1. 完善前端通道登出的端点处理

在Blazor客户端的program.cs中添加一个专门处理前端通道登出的端点,同时通知Blazor Hub用户登出状态:

app.MapGet("/signout-oidc", async (HttpContext context) =>
{
    // 清除本地Cookie
    await context.SignOutAsync("cookies");
    await context.SignOutAsync("oidc");

    // 通知Blazor Hub所有该用户的连接登出
    var hubContext = context.RequestServices.GetRequiredService<IHubContext<BlazorHub>>();
    var userId = context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
    if (!string.IsNullOrEmpty(userId))
    {
        await hubContext.Clients.User(userId).SendAsync("UserLoggedOut");
    }

    return Results.Redirect("/");
});

2. 在Blazor组件中监听登出通知

在MainLayout.razor中注入导航服务和Hub连接,监听登出事件并自动跳转:

@inject NavigationManager NavManager
@inject HubConnection HubConnection
@implements IAsyncDisposable

@code {
    protected override async Task OnInitializedAsync()
    {
        // 监听Hub发送的登出消息
        HubConnection.On("UserLoggedOut", () =>
        {
            NavManager.NavigateTo("/authentication/login", forceLoad: true);
        });

        await HubConnection.StartAsync();
    }

    public async ValueTask DisposeAsync()
    {
        if (HubConnection is not null)
        {
            await HubConnection.DisposeAsync();
        }
    }
}

3. 优化OpenID Connect的登出事件配置

在Blazor客户端的OpenID Connect配置中补充登出回调逻辑:

.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://localhost:7001";
    options.ClientId = "client";
    options.MapInboundClaims = false;
    options.SaveTokens = true;

    options.Events = new OpenIdConnectEvents
    {
        // 登出时传递IdTokenHint给SSO
        OnRedirectToIdentityProviderForSignOut = async context =>
        {
            var idToken = await context.HttpContext.GetTokenAsync("id_token");
            if (!string.IsNullOrEmpty(idToken))
            {
                context.ProtocolMessage.IdTokenHint = idToken;
            }
            await Task.CompletedTask;
        },
        // 登出回调后重定向到登录页
        OnSignedOutCallbackRedirect = context =>
        {
            context.Response.Redirect("/authentication/login");
            context.HandleResponse();
            return Task.CompletedTask;
        }
    };
});

4. 强化AuthorizeView的未授权处理

在MainLayout.razor的AuthorizeView中添加未授权模板,确保未登录状态下自动跳转:

<AuthorizeView>
    <Authorized>
        <!-- 已授权用户的页面内容 -->
        @Body
    </Authorized>
    <NotAuthorized>
        @{
            NavManager.NavigateTo("/authentication/login", forceLoad: true);
        }
    </NotAuthorized>
</AuthorizeView>

内容的提问来源于stack exchange,提问作者Thanh Nguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:05:07