问题描述
我的 AspNet Core 应用程序有问题,我有一个扩展方法来注册配置,在外部库上,如示例所示:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder,SamlSettings settings)
{
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme,options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
在 Startup 类中,在 ConfigureServices 中,我这样调用此方法:
var samlSettings = _configuration.GetSection(SamlSettings.SamlSection).Get<SamlSettings>();
services.AddAuthentication()
.AddSaml2PIdP(samlSettings);
现在不幸的是我不得不做一些改变,所以我需要注入依赖项才能在那个类中执行其他操作,所以我不能再使用静态类和扩展方法,我应该做这样的事情:
public class Saml2PExtension
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public Saml2PExtension(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void AddSaml2PIdP()
{
using var scope = _serviceScopeFactory.CreateScope();
var settings = scope.ServiceProvider.GetrequiredService<SamlSettings>();
var MetadataParser = scope.ServiceProvider.GetrequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetrequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetrequiredService<AuthenticationBuilder>();
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme,options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
}
}
但是我应该如何在 ConfigureServices 方法中注册这个类并调用 configure 方法?
使用新的实现,我不是使用 ServiceProvider 作为 ServiceLocator 反模式吗?
也欢迎可以以更优雅的方式解决问题的替代解决方案。
谢谢
** 编辑 **
最后,我选择保留第一个核心代码片段,将 IWebHostEnvironment 参数添加到扩展方法并手动实例化 SamlMetadataParser,如下所示:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder,IWebHostEnvironment environment,SamlSettings settings)
{
var MetadataParser = new SamlMetadataParser(new ...); // manually create object
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme,options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
// Read Metadata from file ...
var idPOptions = MetadataParser.Pars(xml);
........
}
}
return builder;
}
由于我在组合根,我认为这种方法是正确的。
无论如何感谢您的帮助
解决方法
这可以通过构建您自己的(使用一次)容器来获取您注册的服务。您可以保留您的扩展方法,因为我们通过 IServiceCollection
公开了一个 AuthenticationBuilder
,这非常方便:
public static AuthenticationBuilder AddSaml2PIdP(this AuthenticationBuilder builder,SamlSettings settings)
{
using var scope = builder.Services.BuildServiceProvider().CreateScope();
//get the services
var settings = scope.ServiceProvider.GetRequiredService<SamlSettings>();
var metadataParser = scope.ServiceProvider.GetRequiredService<ISamlMetadataParser>();
var environment = scope.ServiceProvider.GetRequiredService<IWebHostEnvironment>();
var builder = scope.ServiceProvider.GetRequiredService<AuthenticationBuilder>();
//start using the services
foreach (var idP in settings.IdPs)
{
builder.AddSaml2p(idP.Scheme,options =>
{
options.ServiceProviderOptions = SpOptionsHelper.GetSpOptions(settings);
....
}
}
return builder;
}
请注意,这是可能的,但是您需要关心您使用的服务所需的所有依赖项的注册顺序。实际上,您可以将 AddAuthentication
放在 ConfigureServices
的末尾以确保它始终成功。但是如果有什么问题,就会抛出异常,我们可以相应地调整顺序。
更新:
关于可能与 IdentityServer4 中的 IdpOptions
一起使用的后配置(供 OP 试用):
services.AddOptions<IdpOptions>()
.Configure((IdpOptions o,SamlSettings settings,ISamlMetadataParser metadataParser,IWebHostEnvironment environment,AuthenticationBuilder builder) => {
//use your services here to configure next ...
});
实际上上面使用了 OptionsBuilder
,它支持注入最多 5 个依赖项的方法。您可以改用实现 IConfigureOptions<IdpOptions>
或 IPostConfigureOptions<IdpOptions>
的单独类。然后只需使用 services.ConfigureOptions<TImplementation>()
对其进行配置。