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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:03:34