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

Blazor WASM应用同时实现JWT与Azure AD认证的冲突解决问询

问题根源

AddMsalAuthentication方法执行时会自动注册MSAL官方的AuthenticationStateProvider实现,和你自定义的JWT认证状态提供器存在全局注册冲突,二者只会有一个生效,导致另一种登录方式的身份状态无法被正常读取。

解决配置步骤

你可以通过实现复合认证状态提供器兼容两种登录方式,无需修改原有单独运行时的业务逻辑:

1. 保留现有MSAL注册代码

你贴出的Azure AD配置代码无需调整,保持原有配置即可:

builder.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://2913a4cb-b93e-4107-bd31-1f963e967f30/API.Access");
    options.UserOptions.RoleClaim = "roles";
    options.ProviderOptions.Cache.CacheLocation = "localStorage";
    options.ProviderOptions.LoginMode = "redirect";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>();

2. 实现复合认证状态提供器

新建HybridAuthenticationStateProvider,同时对接两种认证逻辑:

public class HybridAuthenticationStateProvider : AuthenticationStateProvider
{
    // 注入MSAL官方认证提供器
    private readonly MsalAuthenticationStateProvider _msalProvider;
    // 注入你原有自定义JWT认证提供器
    private readonly CustomJwtAuthenticationStateProvider _customJwtProvider;

    public HybridAuthenticationStateProvider(MsalAuthenticationStateProvider msalProvider, CustomJwtAuthenticationStateProvider customJwtProvider)
    {
        _msalProvider = msalProvider;
        _customJwtProvider = customJwtProvider;
        // 同步两个提供器的状态变更事件
        _msalProvider.AuthenticationStateChanged += state => NotifyAuthenticationStateChanged(state);
        _customJwtProvider.AuthenticationStateChanged += state => NotifyAuthenticationStateChanged(state);
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        // 优先检查Azure AD登录态
        var msalState = await _msalProvider.GetAuthenticationStateAsync();
        if (msalState.User.Identity?.IsAuthenticated == true)
        {
            return msalState;
        }
        // 无Azure AD登录态则检查自定义JWT登录态
        return await _customJwtProvider.GetAuthenticationStateAsync();
    }
}

3. 调整服务注册顺序

  • 单独注册两个子认证提供器,不要直接覆盖全局AuthenticationStateProvider
  • 最后注册复合提供器作为全局生效的认证状态提供器
// 单独注册自定义JWT提供器本身
builder.Services.AddScoped<CustomJwtAuthenticationStateProvider>();
// 这里放步骤1的MSAL注册代码
// ...
// 注册复合提供器作为全局认证状态提供器
builder.Services.AddScoped<AuthenticationStateProvider, HybridAuthenticationStateProvider>();

4. 适配API调用的授权逻辑

自定义DelegatingHandler,根据登录来源自动附加对应token到请求头:

public class HybridAuthorizationMessageHandler : DelegatingHandler
{
    private readonly IAccessTokenProvider _msalTokenProvider;
    private readonly CustomJwtStore _customJwtStore; // 你自己实现的JWT存储服务
    private readonly AuthenticationStateProvider _authStateProvider;

    public HybridAuthorizationMessageHandler(IAccessTokenProvider msalTokenProvider, CustomJwtStore customJwtStore, AuthenticationStateProvider authStateProvider)
    {
        _msalTokenProvider = msalTokenProvider;
        _customJwtStore = customJwtStore;
        _authStateProvider = authStateProvider;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        if (!authState.User.Identity.IsAuthenticated)
        {
            return await base.SendAsync(request, cancellationToken);
        }

        // 提前在两种登录逻辑里给用户增加auth_type标识Claim,Azure AD加auth_type=azuread,自定义登录加auth_type=custom
        var authType = authState.User.FindFirst("auth_type")?.Value;
        if (authType == "azuread")
        {
            var tokenResult = await _msalTokenProvider.RequestAccessToken();
            if (tokenResult.TryGetToken(out var token))
            {
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.Value);
            }
        }
        else if (authType == "custom")
        {
            var jwt = await _customJwtStore.GetTokenAsync();
            if (!string.IsNullOrEmpty(jwt))
            {
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt);
            }
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

注册HTTP客户端时使用该Handler:

builder.Services.AddScoped<HybridAuthorizationMessageHandler>();
builder.Services.AddHttpClient("YourApiClient", client =>
{
    client.BaseAddress = new Uri(builder.Configuration["ApiBaseUrl"]);
}).AddHttpMessageHandler<HybridAuthorizationMessageHandler>();
配套注意事项
  • 两种登录方式的公共Claim(角色、用户ID、用户名等)要统一命名,避免页面授权校验逻辑出错
  • 退出登录时根据当前用户的auth_type调用对应退出逻辑:Azure AD调用MSAL的登出方法,自定义登录清除本地存储的JWT即可
  • 两种登录方式的有效期校验逻辑保持各自原有实现即可,复合提供器会自动读取生效状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 07:57:04