Asp.net-Core
如何動態設置 OpenIdConnect 中間件選項的權限?
我們有多個租戶,他們使用不同的權限(他們自己的,而不僅僅是標準提供商)。雖然我知道如何動態設置 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>(); }