ash*_*hin 5 identityserver4 asp.net-core-3.1
我们的目标是构建可以对多个租户进行身份验证的多租户身份 server4 应用程序。我们一直在尝试将Finbuckle Mutitenancy与 IdentityServer4集成来实现这一点。已经实施了文档中提到的路由策略的变体来解析每个请求的租户。当客户端请求 IdentityServer 的连接/授权端点(路由 URL 中没有租户标识符)时,身份服务器重定向到的登录 URL 会丢失租户标识符。自定义多租户策略成功执行并以预期方式设置租户信息:使用 http://localhost/Identity/Account/Login 而不是 http://localhost/tenant1/Account/Login。
我们已经提供了我们自己的IMultiTenantStrategy实现来首先从路由中检索租户标识符,如果没有,则从 IdentityServer 的请求参数(自定义参数,从请求令牌的客户端添加的“租户”。)。该MultiTenantMiddleware成功地使用自定义的策略获取承包者标识符,并设置必要的tenantInfo,存储策略等。但随后的连接/授权端点重定向到http://本地主机/身份/帐号/登录跳过租户模板。
但是,如果我们使用FallbackStrategy,它定义了静态租户标识符以在所有其他策略失败时使用,那么授权端点会将浏览器重定向到 http://localhost/tenant1/Account/Login,前提是在 FallbackStrategy 中使用了tenant1静态标识符。这是代码片段:
IdentityServer 的配置服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>();
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddRazorPages(options =>
{
// Since we are using the route multitenant strategy we must add the
// route parameter to the Pages conventions used by Identity.
options.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account", model =>
{
foreach (var selector in model.Selectors)
{
selector.AttributeRouteModel.Template =
AttributeRouteModel.CombineTemplates("{__tenant__}", selector.AttributeRouteModel.Template);
}
});
});
services.DecorateService<LinkGenerator, AmbientValueLinkGenerator>(new List<string> { "__tenant__" });
services.AddMultiTenant()
.WithStrategy<CustomMultiTenantStrategy>(ServiceLifetime.Transient, "__tenant__") // Looks at route first then specific to idserver4's request
//.WithFallbackStrategy("tenant1") // If added, always redirects to tenant1's login page.
.WithConfigurationStore()
.WithRemoteAuthentication()
.WithPerTenantOptions<AuthenticationOptions>((options, tenantInfo) =>
{
// Allow each tenant to have a different default challenge scheme.
if (tenantInfo.Items.TryGetValue("ChallengeScheme", out object challengeScheme))
{
options.DefaultChallengeScheme = (string)challengeScheme;
// options.DefaultSignOutScheme = (string)challengeScheme;
}
})
.WithPerTenantOptions<CookieAuthenticationOptions>((options, tenantInfo) =>
{
// Since we are using the route strategy configure each tenant
// to have a different cookie name and adjust the paths.
options.Cookie.Path = $"/{tenantInfo.Identifier}";
options.Cookie.Name = $"{tenantInfo.Id}_authentication";
options.LoginPath = $"{options.Cookie.Path}{options.LoginPath}";
options.LogoutPath = $"{options.Cookie.Path}{options.LogoutPath}";
});
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(ResourceStore.GetIdentityResources())
.AddInMemoryApiResources(ResourceStore.GetApiResources())
.AddInMemoryClients(ClientStore.Get())
.AddAspNetIdentity<IdentityUser>()
.AddProfileService<CustomProfileService>();
}
Run Code Online (Sandbox Code Playgroud)
IdentityServer 的 Configure 方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseMultiTenant();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{__tenant__=}/{controller=Home}/{action=Index}");
endpoints.MapRazorPages();
});
}
Run Code Online (Sandbox Code Playgroud)
自定义多租户策略:
public class CustomMultiTenantStrategy : IMultiTenantStrategy
{
internal readonly string tenantParam;
private readonly IIdentityServerInteractionService _interactionService;
public CustomMultiTenantStrategy(string tenantParam, IIdentityServerInteractionService interactionService)
{
if (string.IsNullOrWhiteSpace(tenantParam))
{
throw new ArgumentException($"\"{nameof(tenantParam)}\" must not be null or whitespace", nameof(tenantParam));
}
this.tenantParam = tenantParam;
this._interactionService = interactionService;
}
public async Task<string> GetIdentifierAsync(object context)
{
if (!(context is HttpContext))
throw new MultiTenantException(null,
new ArgumentException($"\"{nameof(context)}\" type must be of type HttpContext", nameof(context)));
var httpContext = context as HttpContext;
object identifier = null;
httpContext.Request.RouteValues.TryGetValue(tenantParam, out identifier);
// Fallback to read from the request query params
if (identifier == null)
{
httpContext.Request.Query.TryGetValue("tenant", out StringValues tenant);
if (tenant.Count > 0)
{
identifier = tenant.ToString();
}
else // authorize/connect request would go in here
{
if (httpContext.Request.Query.ContainsKey("returnUrl"))
{
var returnUrl = httpContext.Request.Query["returnUrl"];
var authContext = await this._interactionService.GetAuthorizationContextAsync(returnUrl);
identifier = authContext.Parameters["tenant"];
}
}
}
return await Task.FromResult(identifier as string);
}
}
Run Code Online (Sandbox Code Playgroud)
MVC 客户端的配置服务:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "http://localhost";
options.RequireHttpsMetadata = false;
options.ClientId = "test-ui";
options.ClientSecret = "XXXXX";
options.ResponseType = "code id_token";
options.Scope.Add("custom-profile");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = (ctx) =>
{
var tenant = ctx.HttpContext.Request.IsHttps ? "tenant1" : "tenant2"; // Temporary way to swtich between two tenants - the same mvc client is used by different tenants, in prod, it would be identified based on subdomain.
ctx.ProtocolMessage.Parameters.Add("tenant", tenant );
ctx.ProtocolMessage.AcrValues = $"tenant:{tenant}";
return Task.CompletedTask;
}
};
});
services.AddControllersWithViews();
Run Code Online (Sandbox Code Playgroud)
如果我们从 ConfigureServices 中删除 .WithFallbackStrategy("tenant1"),那么身份服务器将重定向到 http://localhost/Identity/Account/Login 导致 404。我们希望租户名称根据参数动态设置MVC 客户端通过请求参数传递。任何熟悉 Finbuckle.MultiTenant 的人都可以对此有所了解吗?
| 归档时间: |
|
| 查看次数: |
717 次 |
| 最近记录: |