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

Blazor Web App集成Duende IdentityServer自动登出问题求助

问题分析与解决方案

你的核心问题是服务器因闲置回收后,refresh token丢失导致无法自动刷新身份,结合现有配置,以下是关键遗漏点和修复方案:

1. 开启SaveTokens持久化Refresh Token

在Blazor Server的OpenIdConnect配置中,options.SaveTokens = false会导致IDP返回的refresh token无法存储到认证Cookie中,没有持久化的token,服务器回收后凭证直接丢失。

修改ConfigureAuthentication中的OpenIdConnect配置:

options.SaveTokens = true; // 改为true,将access/refresh token保存到Cookie

2. 配置Cookie认证的过期与滑动刷新

Blazor Server依赖Cookie维护会话,默认配置可能存在过期时间过短的问题。需显式配置Cookie过期时间,匹配access token生命周期,并开启滑动刷新延长会话:

builder.Services.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
    options.ExpireTimeSpan = TimeSpan.FromHours(2); // 和access token生命周期保持一致
    options.SlidingExpiration = true; // 闲置时自动延长Cookie过期时间
    options.Cookie.MaxAge = options.ExpireTimeSpan; // 同步Cookie最大存活时间
});

3. 替换内存缓存为持久化分布式缓存

当前使用的AddDistributedMemoryCache()是内存级缓存,服务器进程回收后缓存的token会全部丢失。需改用持久化分布式缓存(如Redis、SQL Server缓存):

// 替换AddDistributedMemoryCache()为Redis缓存配置示例
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "BlazorApp-";
});

4. 完善PersistentAuthenticationStateProvider的token持久化

你的Blazor Client端PersistentAuthenticationStateProvider仅恢复了用户Claims信息,未处理token持久化。服务器回收后,客户端无法获取refresh token,导致无法重新获取凭证。

步骤1:扩展UserInfo类包含token字段

public class UserInfo
{
    // 原有Claims相关字段...
    public string? AccessToken { get; set; }
    public string? RefreshToken { get; set; }
    public DateTimeOffset? AccessTokenExpiresAt { get; set; }

    public static UserInfo FromClaimsPrincipalAndTokens(ClaimsPrincipal principal, AuthenticationProperties properties)
    {
        var userInfo = FromClaimsPrincipal(principal);
        userInfo.AccessToken = properties.GetTokenValue("access_token");
        userInfo.RefreshToken = properties.GetTokenValue("refresh_token");
        userInfo.AccessTokenExpiresAt = properties.GetTokenValue("expires_at") != null 
            ? DateTimeOffset.Parse(properties.GetTokenValue("expires_at")!) 
            : null;
        return userInfo;
    }
}

步骤2:修改Server端OnPersistingAsync方法

private async Task OnPersistingAsync()
{
    var authenticationState = await GetAuthenticationStateAsync();
    var principal = authenticationState.User;
    if (principal.Identity?.IsAuthenticated == true)
    {
        var authProperties = await _httpClient.GetAuthenticationPropertiesAsync();
        persistentComponentState.PersistAsJson(nameof(UserInfo), UserInfo.FromClaimsPrincipalAndTokens(principal, authProperties));
    }
}

5. 检查Duende IDP的Refresh Token配置

确保IDP客户端的Refresh Token生命周期足够长,且开启滑动过期:

new Client
{
    // 原有配置...
    RefreshTokenLifetime = 2592000, // 30天,按需调整
    SlidingRefreshTokenLifetime = 1296000, // 15天,滑动刷新窗口
    RefreshTokenUsage = TokenUsage.ReUse, // 或OneTimeOnly,按需选择
    RefreshTokenExpiration = TokenExpiration.Sliding // 开启滑动过期
}

6. 验证AccessTokenManagement的配置

确保AddOpenIdConnectAccessTokenManagement()配置正确,API请求时使用ITokenClient自动刷新token:

public class AppUsersApiService
{
    private readonly HttpClient _httpClient;
    private readonly ITokenClient _tokenClient;

    public AppUsersApiService(HttpClient httpClient, ITokenClient tokenClient)
    {
        _httpClient = httpClient;
        _tokenClient = tokenClient;
    }

    public async Task<User> Get()
    {
        var token = await _tokenClient.GetAccessTokenAsync("testapi");
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return await _httpClient.GetFromJsonAsync<User>("api/users/me");
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:59:54