Blazor Server结合Azure B2C如何静默刷新用户声明且不中断WebSocket?
Blazor Server + Azure B2C 静默刷新用户声明方案
1. 问题根源分析
你遇到的6分钟声明过期但数小时仍能操作的问题,本质是Blazor Server基于Server端Cookie认证,默认不会主动校验声明的过期时间,且Cookie有效期可能与声明周期不匹配。需要主动触发声明检查与静默刷新逻辑。
2. 基础配置准备
确保Program.cs中正确配置Microsoft.Identity.Web,启用令牌缓存与刷新令牌支持:
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches();
EnableTokenAcquisitionToCallDownstreamApi会自动处理刷新令牌的核心逻辑,后续只需在合适时机触发。
3. 实现静默刷新的两种方案
方案一:组件级声明检查(适合页面/布局组件)
在主布局组件(如MainLayout.razor)中注入相关服务,在初始化时检查声明过期并触发刷新:
@inject ITokenAcquisition TokenAcquisition @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager NavigationManager @code { protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); var user = authState.User; var expClaim = user.FindFirst("exp"); if (expClaim != null) { var expTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(expClaim.Value)); // 提前1分钟触发刷新,避免过期后操作失败 if (expTime <= DateTimeOffset.UtcNow.AddMinutes(1)) { try { // 传入Azure B2C所需的scope,需与应用配置一致 await TokenAcquisition.GetAccessTokenForUserAsync(new[] { "openid", "profile" }); // 通知认证状态更新,刷新组件中的用户信息 ((ServerAuthenticationStateProvider)AuthStateProvider).NotifyAuthenticationStateChanged( AuthStateProvider.GetAuthenticationStateAsync()); } catch (MsalUiRequiredException) { // 无法静默刷新(如刷新令牌过期),跳转至登录页 NavigationManager.NavigateTo("/MicrosoftIdentity/Account/SignIn"); } } } } }
方案二:全局中间件检查(适合全站点统一校验)
创建全局中间件,在每个请求进入时自动检查并刷新:
public class TokenRefreshMiddleware { private readonly RequestDelegate _next; public TokenRefreshMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, ITokenAcquisition tokenAcquisition, AuthenticationStateProvider authStateProvider) { var user = context.User; if (user.Identity.IsAuthenticated) { var expClaim = user.FindFirst("exp"); if (expClaim != null) { var expTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(expClaim.Value)); if (expTime <= DateTimeOffset.UtcNow.AddMinutes(1)) { try { await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "openid", "profile" }); ((ServerAuthenticationStateProvider)authStateProvider).NotifyAuthenticationStateChanged( authStateProvider.GetAuthenticationStateAsync()); } catch (MsalUiRequiredException) { context.Response.Redirect("/MicrosoftIdentity/Account/SignIn"); return; } } } } await _next(context); } }
在Program.cs中注册中间件(注意顺序:放在UseAuthentication和UseAuthorization之后):
app.UseAuthentication(); app.UseAuthorization(); // 注册令牌刷新中间件 app.UseMiddleware<TokenRefreshMiddleware>(); app.UseRouting(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host");
4. 同步Cookie与声明有效期
为彻底解决“过期后仍能操作”的问题,可将Cookie有效期与声明周期绑定:
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches() .AddCookie(options => { // 与id_token过期时间保持一致 options.ExpireTimeSpan = TimeSpan.FromMinutes(6); // 滑动过期,每次请求自动刷新Cookie有效期 options.SlidingExpiration = true; });
5. 注意事项
- 确保Azure B2C应用注册中,
Authentication->Advanced settings->Allow public client flows设置为Yes; - 生产环境建议替换内存令牌缓存为分布式缓存(如Redis),避免服务器重启后丢失令牌;
- 捕获
MsalUiRequiredException时,需处理用户重新登录的场景,避免无提示阻塞操作。
内容的提问来源于stack exchange,提问作者Fredou
相关产品推荐
相关产品推荐

