首页 > 解决方案 > 在类中实例化 IConfidentialClientApplication 是不好的做法吗?

问题描述

例如,我AuthorizationClient在我的应用程序配置中注册了一个临时服务,它充当我的 API 和 Azure 之间的授权令牌的客户端中介。在里面,有一个 IConfidentialClientApplication对象属性:

public class AuthorizationClient : IAuthorizationClient
{
    private readonly string[] _resourceIds;
    private IConfidentialClientApplication App;

    public AuthorizationClient(IAuthenticationConfig AuthenticationConfig, IConfiguration configuration)
    {
        var scope = "/.default";
        var resourceId = "api://" + configuration[AuthenticationConfig.ResourceID] + scope;
        var clientId = configuration[AuthenticationConfig.ClientID];
        var clientSecret = configuration[AuthenticationConfig.ClientSecret];            
        var instance = AuthenticationConfig.Instance;
        var tenant = configuration[AuthenticationConfig.AzureTenantId];        

        var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
            
        _resourceIds = new string[] { resourceId };
        try
        {
            App = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithClientSecret(clientSecret)
            .WithAuthority(new Uri(authority))                
            .Build();
        }
        catch(Exception ex)
        {
            //TODO logger
        }

    }
...
}

在这个类的其他地方,我有成功获取令牌、检查令牌缓存等的方法。

但是当我看到IConfidentialClientApplication正在使用的文档或存储库时,它总是在应用程序级别作为服务添加,而不是在另一个对象中。我这样做的方式是否存在潜在的陷阱?

IConfidentialClientApplication如果每个客户端请求都实例化一个新的,这似乎相当重/大,但是当我在一个应用级服务的对象中实例化它时,每个生命周期似乎只创建一个。

标签: c#performanceasp.net-corememorymsal

解决方案


在目标类中实例化依赖是一种代码味道

陷阱:与实现细节的紧密耦合和显式依赖原则违规。

这些将使孤立地维护和测试您的课程变得困难。

通过应用单一责任原则/关注点分离和显式依赖原则,可以修复这些设计问题,使您的代码更加可靠

创建一个类来存储客户端选项。根据提供的示例,它可能看起来像这样

public class AuthorizationClientOptions {
    public string[] ResourceIds { get; set; }
}

重构类以显式依赖于它实际需要什么来执行其功能

public class AuthorizationClient : IAuthorizationClient {
    private readonly string[] _resourceIds;
    private readonly IConfidentialClientApplication app;

    public AuthorizationClient(IConfidentialClientApplication app, IOptions<AuthorizationClientOptions> options) {
        this.app = app;
        _resourceIds = options.Value.ResourceIds;
    }
    
    // ...
}

在组合根中配置必要的实现细节(启动)

//...

IConfiguration Configuration; //Populated either via constructor injection or manually 

public void ConfigureServices(IServiceCollection services) {

    //...
    
    //Configure options
    services.AddOptions<AuthorizationClientOptions>()
        .Configure<IAuthenticationConfig,IConfiguration>(
            (o, authConfig, config) => {
                var scope = "/.default";
                var resourceId = "api://" + config[authConfig.ResourceID] + scope;
                o.ResourceIds = new string[] { resourceId };
            });
            
    //Configure dependency using factory delegate
    services.AddSingleton<IConfidentialClientApplication>(sp => {
        IAuthenticationConfig AuthenticationConfig = sp.GetRequiredService<IAuthenticationConfig>();
        
        var instance = Configuration.Instance;
        var tenant = Configuration[AuthenticationConfig.AzureTenantId];
        var authority = string.Format(CultureInfo.InvariantCulture, instance, tenant);
        
        var clientId = Configuration[AuthenticationConfig.ClientID];
        var clientSecret = Configuration[AuthenticationConfig.ClientSecret];
        
        return ConfidentialClientApplicationBuilder.Create(clientId)
            .WithClientSecret(clientSecret)
            .WithAuthority(new Uri(authority))
            .Build();
    });
    
    services.AddScoped<IAuthorizationClient, AuthorizationClient>();
    
    //...
}

IConfidentialClientApplication如果每个客户端请求都实例化一个新的,这似乎相当重/大

上面示例中的IConfidentialClientApplication是作为单例创建的,因此在实例化时将具有一次性成本。


推荐阅读