在没有 BuildServiceProvider() 的情况下在 AddOpenIdConnect 中获取 ServiceProvider

Get ServiceProvider inside AddOpenIdConnect without BuildServiceProvider()

有没有什么好的方法可以在 AddOpenIdConnect 中获取 ServiceProvider 稍后在我们已完全设置 DI 容器的地方配置 ClientSecret? (例如 Configure(IApplicationBuilder app)

我们正在从其他地方获取客户端机密,我们喜欢为此使用 DI。

目前我们这样做,但我真的很想删除 services.BuildServiceProvider()

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddOpenIdConnect(AuthenticationScheme, options =>
    {
        ServiceProvider serviceProvider = services.BuildServiceProvider(); // we like to prevent this
        options.ClientSecret = serviceProvider.GetRequiredService<ISecretRetriever>().GetClientSecret();

备注

对于像 OnValidatePrincipal 这样的事件,我们可以从 CookieValidatePrincipalContext.HttpContext.RequestServices

中获取

使用 services.BuildServiceProvider() 会给出这个警告:

warning "Calling 'BuildServiceProvider' from application code results in a additional copy of Singleton services being created"

认证配置系统使用Options pattern。这意味着以下方法将对您的问题中显示的方法产生 相似 效果:

services.AddAuthentication()
    .AddOpenIdConnect(AuthenticationScheme, options =>
    {
        // ...
    });

services.Configure<OpenIdConnectOptions>(AuthenticationScheme, options =>
{
    options.ClientSecret = "ClientSecret";
});

这很有用,因为选项模式 supports DI,使用如下内容:

services.AddOptions<OpenIdConnectOptions>(AuthenticationScheme)
    .Configure<ISecretRetriever>((options, secretRetriever) =>
    {
        options.ClientSecret = secretRetriever.GetClientSecret();
    });

要访问使用 DI 的 Configure 方法,您必须先调用 AddOptions。在此示例中,Configure 被赋予一个类型参数,它表示所需的依赖项。这将作为第二个参数传递到您的配置回调中,位于正在配置的 OpenIdConnectOptions 实例之后。

以柯克的回答为基础,提供更完整的示例

// Register our custom options -- we can resolve this as IOptions<MyCustomOptions>
services.Configure<MyCustomOptions>(config.GetSection(optionsSection));

// Configure OIDC
var authBuilder = services.AddAuthentication()
    .AddOpenIdConnect(schemeName, options => ...);

// Update OIDC config with our custom options
authBuilder.Services.AddOptions<OpenIdConnectOptions>(schemeName)
    .Configure<IOptions<MyCustomOptions>>((oidcOptions, myOptions) =>
    {
        oidcOptions.ClientSecret = myOptions.Value.SecretTime;
    });

// register some other services
builder.Services.AddSingleton<Foo>(sp => {
    var myOpts = sp.GetRequiredService<IOptions<MyCustomOptions>>();
    new Foo(myOpts.Value.SecretTime)
};