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

.NET MAUI中为Azure B2C多用户流配置多IPublicClientApplication实例的正确方法

问题原因

.NET MAUI使用的Microsoft.Extensions.DependencyInjection容器中,同一服务类型的多次注册会被最后一次注册覆盖。你两次注册IPublicClientApplication类型,最终所有依赖该类型的服务都会拿到最后注册的密码重置实例,这就是两个认证服务共用同一实例的根本原因。

解决方案

下面提供三种可行的解决方式,按简洁度和推荐度排序:

方案1:直接在服务注册时传入实例(最简洁)

不需要修改现有认证服务的代码,直接在注册服务时创建对应IPublicClientApplication实例并传入构造函数:

// 创建登录策略对应的实例
var signInApp = PublicClientApplicationBuilder
    .Create(Constants.ClientId)
    .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
    .WithRedirectUri($"msal{Constants.ClientId}://auth")
    .WithB2CAuthority(Constants.AuthoritySignIn)
    .Build();
// 注册登录服务,直接传入实例
mauiAppBuilder.Services.AddScoped<IAuthService>(sp => new AuthServiceB2CSignInSignUp(signInApp));

// 创建密码重置策略对应的实例
var resetPasswordApp = PublicClientApplicationBuilder
    .Create(Constants.ClientId)
    .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
    .WithRedirectUri($"msal{Constants.ClientId}://auth")
    .WithB2CAuthority(Constants.AuthorityPasswordReset)
    .Build();
// 注册密码重置服务,直接传入实例
mauiAppBuilder.Services.AddScoped<IAuthServiceB2CResetPassword>(sp => new AuthServiceB2CResetPassword(resetPasswordApp));

方案2:使用Keyed Services(.NET 6+官方推荐)

利用.NET 6引入的Keyed Services特性,为不同实例指定唯一标识,在注入时通过标识区分:

步骤1:修改服务注册

// 注册登录用的IPublicClientApplication,指定键为"SignIn"
mauiAppBuilder.Services.AddKeyedScoped<IPublicClientApplication>("SignIn", sp =>
{
    return PublicClientApplicationBuilder
        .Create(Constants.ClientId)
        .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
        .WithRedirectUri($"msal{Constants.ClientId}://auth")
        .WithB2CAuthority(Constants.AuthoritySignIn)
        .Build();
});

// 注册密码重置用的IPublicClientApplication,指定键为"ResetPassword"
mauiAppBuilder.Services.AddKeyedScoped<IPublicClientApplication>("ResetPassword", sp =>
{
    return PublicClientApplicationBuilder
        .Create(Constants.ClientId)
        .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
        .WithRedirectUri($"msal{Constants.ClientId}://auth")
        .WithB2CAuthority(Constants.AuthorityPasswordReset)
        .Build();
});

// 正常注册认证服务
mauiAppBuilder.Services.AddScoped<IAuthService, AuthServiceB2CSignInSignUp>();
mauiAppBuilder.Services.AddScoped<IAuthServiceB2CResetPassword, AuthServiceB2CResetPassword>();

步骤2:修改认证服务构造函数

通过[FromKeyedServices]属性指定要注入的实例标识:

// 登录服务构造函数
public class AuthServiceB2CSignInSignUp : IAuthService
{
    private readonly IPublicClientApplication _signInApp;

    public AuthServiceB2CSignInSignUp([FromKeyedServices("SignIn")] IPublicClientApplication signInApp)
    {
        _signInApp = signInApp;
    }

    // 原有方法保持不变
}

// 密码重置服务构造函数
public class AuthServiceB2CResetPassword : IAuthServiceB2CResetPassword
{
    private readonly IPublicClientApplication _resetApp;

    public AuthServiceB2CResetPassword([FromKeyedServices("ResetPassword")] IPublicClientApplication resetApp)
    {
        _resetApp = resetApp;
    }

    // 原有方法保持不变
}

方案3:创建包装类区分实例(兼容旧版本)

如果需要兼容.NET 6以下版本,可以创建专属包装类来区分不同的IPublicClientApplication实例:

步骤1:创建包装类

// 登录策略专用包装类
public class SignInPublicClientApplication : IPublicClientApplication
{
    private readonly IPublicClientApplication _innerApp;

    public SignInPublicClientApplication(IPublicClientApplication innerApp)
    {
        _innerApp = innerApp;
    }

    // 实现IPublicClientApplication的所有接口方法,直接委托给_innerApp
    public Task<IEnumerable<IAccount>> GetAccountsAsync(string? policy = null)
        => _innerApp.GetAccountsAsync(policy);

    public AcquireTokenInteractiveParameterBuilder AcquireTokenInteractive(IEnumerable<string> scopes)
        => _innerApp.AcquireTokenInteractive(scopes);

    // 其他接口方法同理,全部委托给_innerApp
}

// 密码重置策略专用包装类
public class ResetPasswordPublicClientApplication : IPublicClientApplication
{
    private readonly IPublicClientApplication _innerApp;

    public ResetPasswordPublicClientApplication(IPublicClientApplication innerApp)
    {
        _innerApp = innerApp;
    }

    // 同样实现所有IPublicClientApplication接口方法,委托给_innerApp
}

步骤2:修改服务注册

// 注册登录用的包装实例
mauiAppBuilder.Services.AddScoped<SignInPublicClientApplication>(sp =>
{
    var app = PublicClientApplicationBuilder
        .Create(Constants.ClientId)
        .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
        .WithRedirectUri($"msal{Constants.ClientId}://auth")
        .WithB2CAuthority(Constants.AuthoritySignIn)
        .Build();
    return new SignInPublicClientApplication(app);
});

// 注册密码重置用的包装实例
mauiAppBuilder.Services.AddScoped<ResetPasswordPublicClientApplication>(sp =>
{
    var app = PublicClientApplicationBuilder
        .Create(Constants.ClientId)
        .WithIosKeychainSecurityGroup(Constants.IosKeychainSecurityGroups)
        .WithRedirectUri($"msal{Constants.ClientId}://auth")
        .WithB2CAuthority(Constants.AuthorityPasswordReset)
        .Build();
    return new ResetPasswordPublicClientApplication(app);
});

// 注册认证服务
mauiAppBuilder.Services.AddScoped<IAuthService, AuthServiceB2CSignInSignUp>();
mauiAppBuilder.Services.AddScoped<IAuthServiceB2CResetPassword, AuthServiceB2CResetPassword>();

步骤3:修改认证服务构造函数

注入对应的包装类:

// 登录服务构造函数
public class AuthServiceB2CSignInSignUp : IAuthService
{
    private readonly IPublicClientApplication _signInApp;

    public AuthServiceB2CSignInSignUp(SignInPublicClientApplication signInApp)
    {
        _signInApp = signInApp;
    }

    // 原有方法保持不变
}

// 密码重置服务构造函数
public class AuthServiceB2CResetPassword : IAuthServiceB2CResetPassword
{
    private readonly IPublicClientApplication _resetApp;

    public AuthServiceB2CResetPassword(ResetPasswordPublicClientApplication resetApp)
    {
        _resetApp = resetApp;
    }

    // 原有方法保持不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 10:37:01