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

Blazor WASM页面刷新后API授权丢失问题求助

Blazor WASM Azure AD认证:刷新/跨标签页授权丢失问题解决

问题场景

基于.NET 8.0 Blazor WASM应用,使用自带的MSAL认证模板集成Azure AD,本地IIS部署API,通过私有NuGet分发API客户端。应用正常导航时可正常访问认证接口,但刷新产品详情页或新标签打开链接时,出现UnauthorizedAccessException: Read action not allowed,此时sessionStorage存在有效token,但API请求未携带授权头。

关键代码片段

Program.cs

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});

var authority = new AuthorityProvider();
var productsApiClient = new ProductsApiClient(
        authority,
        new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }
    );

builder.Services.AddScoped<IProductsApiClient>(sp => productsApiClient);

await builder.Build().RunAsync();    

组件API调用

@page "/products/{Id:guid}"
@using Microsoft.AspNetCore.Authorization
@using Products.Contracts
@using Products.Contracts.API
@inject IProductsApiClient ApiClient
@attribute [Authorize]

@code {
  [Parameter]
  public Guid Id { get; set; } = default!;
  private Product product { get; set; } = default!;

  protected override async Task OnInitializedAsync()
  {
    product= await ApiClient.GetProduct(Id);
  }
}

API后端权限校验

public async Task<Product> Getproduct(Guid id)
{
    if (!Authority.GetAllowed)
        throw new UnauthorizedAccessException(READ_ACTION_NOT_ALLOWED);

    var url = $"{this.BaseRoute}/{id}";
    var result = await _http.GetFromJsonAsync<Product>(url);

    if (result == null)
        return Product.Empty();

    this.Authority.Update(result.Links);
    return result;
}

问题原因分析

  1. HttpClient未集成MSAL token拦截逻辑:手动new HttpClient没有使用Blazor WASM认证系统自动配置的HttpClient实例——该实例带有MSAL消息处理器,负责自动为请求附加Bearer token。刷新时,手动创建的HttpClient无法获取当前认证状态的token,导致请求无授权头。
  2. 提前实例化依赖服务:AuthorityProvider和ProductsApiClient在Program.cs中同步实例化,而Blazor WASM的认证状态加载是异步过程。刷新页面时,认证状态尚未就绪,AuthorityProvider无法获取有效的用户权限信息,触发权限校验失败。
  3. 认证状态初始化时机不匹配:客户端导航时,认证状态已在应用启动时完成加载;但刷新或新标签打开时,应用重新初始化,组件的OnInitializedAsync会在认证状态就绪前执行,导致API调用时无有效授权信息。

跨标签页/页面刷新的授权持久化方案

1. 使用DI托管的HttpClient替换手动实例

Blazor WASM的AddMsalAuthentication会自动注册带有token拦截逻辑的HttpClient,通过DI注入该实例,确保请求自动携带Bearer token:

// 移除手动创建的HttpClient和ApiClient实例
builder.Services.AddScoped<AuthorityProvider>();
builder.Services.AddScoped<IProductsApiClient>(sp => 
    new ProductsApiClient(
        sp.GetRequiredService<AuthorityProvider>(),
        sp.GetRequiredService<HttpClient>()
    )
);

2. 等待认证状态就绪后再调用API

在组件中注入AuthenticationStateProvider,确保认证状态加载完成后再执行API调用:

@inject AuthenticationStateProvider AuthStateProvider

@code {
    protected override async Task OnInitializedAsync()
    {
        // 等待认证状态初始化完成
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        if (authState.User.Identity?.IsAuthenticated ?? false)
        {
            product = await ApiClient.GetProduct(Id);
        }
    }
}

3. 配置MSAL缓存持久化

显式配置MSAL使用sessionStorage存储token,确保跨标签页共享缓存:

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    // 确保缓存存储在sessionStorage,支持跨标签页共享
    options.ProviderOptions.Cache.CacheLocation = "sessionStorage";
});

4. 调整AuthorityProvider权限加载逻辑

修改AuthorityProvider,使其在认证状态就绪后从token中解析权限,或在API调用前触发权限初始化,避免提前判断权限导致的错误。


正常导航时的授权传递机制

  • 客户端路由特性:正常导航是Blazor的客户端路由跳转,应用无需重新初始化,认证状态已在启动时完成加载。
  • MSAL自动附加token:通过DI注入的HttpClient带有MSAL消息处理器,会自动拦截所有API请求,从MSAL缓存中获取有效Bearer token,并添加到Authorization请求头中。
  • 权限状态已初始化:正常导航时,AuthorityProvider已通过之前的API请求完成权限信息更新,权限校验逻辑可正常通过。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 16:05:15