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

Blazor WASM Azure AD 添加自定义声明的实现方案咨询

为Azure AD认证的Blazor WASM应用添加自定义声明

针对你的需求,这里提供几个可行的方案,无需依赖预览版Azure自定义声明或ASP.NET的IClaimsTransformation类:

方案1:登录成功回调中注入声明

利用Azure AD登录后的回调事件,在用户完成认证后立即调用独立API获取额外声明,并更新当前用户主体:

  1. 在Program.cs配置Azure AD认证时,注册OnAuthenticationSucceeded回调:
builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    // 添加API访问所需的权限范围
    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://your-api-resource-id/claims.read");

    options.Events.OnAuthenticationSucceeded = async context =>
    {
        // 获取认证后的访问令牌,用于调用独立API
        var accessToken = context.AccessToken;
        using var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        // 调用API获取额外声明(示例返回ClaimDto列表,包含Type和Value字段)
        var extraClaims = await httpClient.GetFromJsonAsync<List<ClaimDto>>("https://your-api-domain/api/user/claims");

        // 复制原有身份声明,添加新声明
        var updatedIdentity = new ClaimsIdentity(context.Principal.Identity);
        foreach (var claim in extraClaims)
        {
            updatedIdentity.AddClaim(new Claim(claim.Type, claim.Value));
        }

        // 更新当前用户主体
        context.Principal = new ClaimsPrincipal(updatedIdentity);
    };
});
  1. 注意事项:需确保API仅接受携带有效Azure AD令牌的请求,同时处理API调用失败的异常场景(如令牌过期、服务不可用)。

方案2:自定义AuthenticationStateProvider

通过继承MsalAuthenticationStateProvider,重写GetAuthenticationStateAsync方法,全局拦截用户身份状态的获取逻辑,自动注入额外声明:

  1. 创建自定义认证状态提供类:
public class CustomAuthStateProvider : MsalAuthenticationStateProvider
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IConfiguration _config;

    public CustomAuthStateProvider(ILoggerFactory loggerFactory, 
                                   IConfiguration config, 
                                   IHttpClientFactory httpClientFactory) 
        : base(loggerFactory, config)
    {
        _httpClientFactory = httpClientFactory;
        _config = config;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var originalState = await base.GetAuthenticationStateAsync();
        var user = originalState.User;

        if (user.Identity.IsAuthenticated)
        {
            // 请求API访问令牌
            var tokenRequest = new[] { _config["ApiSettings:ClaimScope"] };
            var tokenResult = await GetAccessTokenAsync(tokenRequest);
            
            if (tokenResult.TryGetToken(out var token))
            {
                var httpClient = _httpClientFactory.CreateClient();
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
                
                // 获取额外声明
                var extraClaims = await httpClient.GetFromJsonAsync<List<ClaimDto>>($"{_config["ApiSettings:BaseUrl"]}/api/user/claims");

                // 更新身份声明
                var updatedIdentity = new ClaimsIdentity(user.Identity);
                foreach (var claim in extraClaims)
                {
                    updatedIdentity.AddClaim(new Claim(claim.Type, claim.Value));
                }

                return new AuthenticationState(new ClaimsPrincipal(updatedIdentity));
            }
        }

        return originalState;
    }
}
  1. 在Program.cs中替换默认的认证状态提供程序:
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
  1. 优化建议:可添加声明缓存逻辑(如存入localStorage),避免每次获取状态都调用API,提升性能。

方案3:组件/页面初始化时添加声明

如果仅需在特定页面或组件中注入声明,可在组件初始化阶段完成操作:

@inject AuthenticationStateProvider AuthStateProvider
@inject HttpClient HttpClient
@inject IConfiguration Config

protected override async Task OnInitializedAsync()
{
    var authState = await AuthStateProvider.GetAuthenticationStateAsync();
    var user = authState.User;

    if (user.Identity.IsAuthenticated)
    {
        var msalProvider = (MsalAuthenticationStateProvider)AuthStateProvider;
        var tokenResult = await msalProvider.GetAccessTokenAsync(new[] { Config["ApiSettings:ClaimScope"] });
        
        if (tokenResult.TryGetToken(out var token))
        {
            HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
            var extraClaims = await HttpClient.GetFromJsonAsync<List<ClaimDto>>($"{Config["ApiSettings:BaseUrl"]}/api/user/claims");

            var updatedIdentity = new ClaimsIdentity(user.Identity);
            foreach (var claim in extraClaims)
            {
                updatedIdentity.AddClaim(new Claim(claim.Type, claim.Value));
            }

            // 通知认证状态变更,更新组件上下文的用户主体
            msalProvider.NotifyAuthenticationStateChanged(
                Task.FromResult(new AuthenticationState(new ClaimsPrincipal(updatedIdentity)))
            );
        }
    }
}

通用注意事项

  • 声明持久化:若需页面刷新后保留自定义声明,可将声明序列化为JSON存入localStorage,在登录或初始化时读取并重新添加到用户主体。
  • 权限验证:添加的声明可直接用于[Authorization(Roles = "Admin")]或自定义策略的权限检查,与原生声明行为一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:07:36