我有一个ASP.NET Core 3应用程序,并且我正在使用AzureAD进行身份验证。我的Startup.ConfigureSerivces方法中包含以下几行,其目的是在附加Cookie时执行一些业务规则。
services.Configure<CookiePolicyOptions>(options => {
options.CheckConsentNeeded = ctx => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.OnAppendCookie = ctx => {
var svc = ctx.Context.RequestServices.GetRequiredService<IUserInformationService>();
// do something with svc
};
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => {
Configuration.Bind("AzureAd", options);
});
这很好,并且在IUserInformationService
方法中按预期方式获得了注入的OnAppendCookie
对象,并且从appsettings.json中获取了AzureAD信息。
但是,最近,有关AzureAD租户的信息一定不能驻留在appsettings.json中,而是我现在必须查阅数据库。我有一项已经查询数据库并获取AD设置的服务。像这样:
public interface IAzureADConfiguration {
void Configure(AzureADOptions options);
}
但是,在调用AddAzureAD
时,我找不到一种方法来检索注入的服务。我想要的是这样的:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => {
var svc = ???
svc.Configure(options);
Configuration.Bind("AzureAd", options);
});
由于我无法像在HttpContext
中那样在AddAzureAD
方法中访问OnAppendCookie
。在此阶段是否有办法获取注入的对象?我不想对实例进行硬编码,因为此要求将来可能会更改(即添加另一种方法来配置从何处获取Azure广告设置)。预先感谢!
答案 0 :(得分:2)
请@Nkosi指出相关文档:Use DI services to configure options
他们的答案使我正确地开始了,但是所提供的代码示例对我而言不起作用,因为它没有说明命名选项(AzureAD配置使用该选项)。在弄清楚并简化一点之后,我得到了以下内容...
对于AzureADOptions
,最好的选择似乎是添加后配置以在提供给AddAzureAD
的现有(必需)配置回调之后运行。两个回调都将传递到相同的AzureADOptions
实例,因此可以按原样保留现有回调以加载“基本”值,并且配置后回调可以根据需要覆盖它们。
// Configure Azure AD to load settings from the app config.
// This is unchanged (apart from syntax) from the original question.
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
// Add a post-configure callback to augment the app config-provided values
// with service-provided values.
services.AddOptions<AzureADOptions>(AzureADDefaults.AuthenticationScheme)
.PostConfigure<IAzureADConfiguration>((options, service) =>
service.Configure(options));
如果您不想从应用程序配置中加载基本值,只需将Configuration.Bind("AzureAD", options)
替换为空块({ }
)。
AddAzureAD
使用命名的选项实例,因此请确保将相同的名称传递给PostConfigure
和AddAuthentication
。如果两个地方都没有使用相同的名称,则配置后回调将不会配置正确的选项实例(或根本不会运行)。
注意:如果有人想使用此答案来配置Azure AD以外的其他东西...如果没有注册现有的选项实例或回调,则应使用Configure
方法而不是PostConfigure
,并且您可能不需要提供名称。
services.AddOptions<MyOptionsType>()
.Configure<IMyConfigurationService>((options, service) =>
service.Configure(options));
虽然这不是您问题的直接答案,但这可能是解决引发您问题的问题的可行解决方案。
您可以实现custom configuration provider来从数据库中加载设置,而不是编写配置服务。然后,您应该可以使用AddAzureAD
离开Configuration.Bind
通话。
文档new
中的示例建立了EF数据上下文,而不是由DI容器提供它。这违反了DI的直觉,但我认为在这种情况下还可以。示例提供程序是一个小型且功能齐全的子系统。它的数据库配置仍然被注入(尽管不是通过DI容器),并且仍然可以通过注入EF内存数据库的配置来对其进行单元测试。它没有其他依赖项可以交换。 DI容器不会增加太多价值-系统已经具有它需要的所有依赖项注入。
答案 1 :(得分:2)
内部AddAzureAD
最终将在Configure
上调用AzureADOptions
。
要进入该过程,请根据您的服务创建自定义IConfigureOptions<T>
public class AzureADOptionsSetup : IConfigureOptions<AzureADOptions> {
private readonly IAzureADConfiguration service;
public AzureADOptionsSetup(IAzureADConfiguration service) {
this.service = service;
}
public void Configure(AzureADOptions options) {
service.Configure(options);
}
}
并确保将其注册到服务集合中
services.AddTransient<IConfigureOptions<AzureADOptions>, AzureADOptionsSetup>();
这将确保您的自定义选项配置有机会在options类上进行操作。