Asp.net-Core

如何動態設置 OpenIdConnect 中間件選項的權限?

  • October 23, 2018

我們有多個租戶,他們使用不同的權限(他們自己的,而不僅僅是標準提供商)。雖然我知道如何動態設置 clientId 和 secret,但我不知道如何設置權限。它在啟動期間設置一次,之後無法更改(或者看起來如此)。

由於我們有很多租戶,我們不想在啟動時全部註冊,而且我們也不想在添加租戶時要求重新啟動。

有什麼建議可以解決這個問題嗎?我很想使用現有的中間件,但如果不可能,我可以自己編寫。

感謝任何建議!

雖然有點棘手,但這絕對是可能的。這是一個簡化的範例,使用 MSFT OIDC 處理程序、自定義監視器和基於路徑的租戶解析:

實現您的租戶解析邏輯。例如:

public class TenantProvider
{
   private readonly IHttpContextAccessor _httpContextAccessor;

   public TenantProvider(IHttpContextAccessor httpContextAccessor)
       => _httpContextAccessor = httpContextAccessor;

   public string GetCurrentTenant()
   {
       // This sample uses the path base as the tenant.
       // You can replace that by your own logic.
       string tenant = _httpContextAccessor.HttpContext.Request.PathBase;
       if (string.IsNullOrEmpty(tenant))
       {
           tenant = "default";
       }

       return tenant;
   }
}
public void Configure(IApplicationBuilder app)
{
   app.Use(next => context =>
   {
       // This snippet uses a hardcoded resolution logic.
       // In a real world app, you'd want to customize that.
       if (context.Request.Path.StartsWithSegments("/fabrikam", out PathString path))
       {
           context.Request.PathBase = "/fabrikam";
           context.Request.Path = path;
       }

       return next(context);
   });

   app.UseAuthentication();

   app.UseMvc();
}

實現自定義IOptionsMonitor<OpenIdConnectOptions>

public class OpenIdConnectOptionsProvider : IOptionsMonitor<OpenIdConnectOptions>
{
   private readonly ConcurrentDictionary<(string name, string tenant), Lazy<OpenIdConnectOptions> _cache;
   private readonly IOptionsFactory<OpenIdConnectOptions> _optionsFactory;
   private readonly TenantProvider _tenantProvider;

   public OpenIdConnectOptionsProvider(
       IOptionsFactory<OpenIdConnectOptions> optionsFactory,
       TenantProvider tenantProvider)
   {
       _cache = new ConcurrentDictionary<(string, string), Lazy<OpenIdConnectOptions>();
       _optionsFactory = optionsFactory;
       _tenantProvider = tenantProvider;
   }

   public OpenIdConnectOptions CurrentValue => Get(Options.DefaultName);

   public OpenIdConnectOptions Get(string name)
   {
       var tenant = _tenantProvider.GetCurrentTenant();

       Lazy<OpenIdConnectOptions> Create() => new Lazy<OpenIdConnectOptions>(() => _optionsFactory.Create(name));
       return _cache.GetOrAdd((name, tenant), _ => Create()).Value;
   }

   public IDisposable OnChange(Action<OpenIdConnectOptions, string> listener) => null;
}

實現自定義IConfigureNamedOptions<OpenIdConnectOptions>

public class OpenIdConnectOptionsInitializer : IConfigureNamedOptions<OpenIdConnectOptions>
{
   private readonly IDataProtectionProvider _dataProtectionProvider;
   private readonly TenantProvider _tenantProvider;

   public OpenIdConnectOptionsInitializer(
       IDataProtectionProvider dataProtectionProvider,
       TenantProvider tenantProvider)
   {
       _dataProtectionProvider = dataProtectionProvider;
       _tenantProvider = tenantProvider;
   }

   public void Configure(string name, OpenIdConnectOptions options)
   {
       if (!string.Equals(name, OpenIdConnectDefaults.AuthenticationScheme, StringComparison.Ordinal))
       {
           return;
       }

       var tenant = _tenantProvider.GetCurrentTenant();

       // Create a tenant-specific data protection provider to ensure
       // encrypted states can't be read/decrypted by the other tenants.
       options.DataProtectionProvider = _dataProtectionProvider.CreateProtector(tenant);

       // Other tenant-specific options like options.Authority can be registered here.
   }

   public void Configure(OpenIdConnectOptions options)
       => Debug.Fail("This infrastructure method shouldn't be called.");
}

在 DI 容器中註冊服務:

public void ConfigureServices(IServiceCollection services)
{
   // ...

   // Register the OpenID Connect handler.
   services.AddAuthentication()
       .AddOpenIdConnect();

   services.AddSingleton<TenantProvider>();
   services.AddSingleton<IOptionsMonitor<OpenIdConnectOptions>, OpenIdConnectOptionsProvider>();
   services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, OpenIdConnectOptionsInitializer>();
}

引用自:https://stackoverflow.com/questions/52955238