如何解决IdenityServer和每个租户策略的实现数据库
我正在尝试实现多租户IdentityServer,每个租户都有自己的数据库。
客户端我正在使用Angular,我正在使用tenantId
传递我的oidc-client
,就像这样:
角度
import { UserManager,UserManagerSettings,User } from "oidc-client";
private manager = new UserManager(getClientSettings());
login() {
return this.manager.signinRedirect({
extraQueryParams: {
tenant: AppConfig.settings.connectionApiData.tenant,},});
}
AppIdentityDbContext
public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
private readonly IApplicationService _applicationService;
private readonly HttpContext _httpContext;
private readonly ITenantProvider provider;
private readonly Tenant.Tenant _tenant;
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options,ITenantProvider provider,IHttpContextAccessor httpContextAccessor,IApplicationService applicationService) : base(options)
{
_tenant = provider.GetTenantById(_applicationService.TenantId);
_httpContext = httpContextAccessor.HttpContext;
_applicationService = applicationService;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example,you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole
{Name = Constants.Roles.Consumer,NormalizedName = Constants.Roles.Consumer.ToUpper()});
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(_tenant.DatabaseConnectionString);
base.OnConfiguring(optionsBuilder);
}
}
TenantProvider
public class Tenant
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Host { get; set; }
public string DatabaseConnectionString { get; set; }
}
public interface ITenantProvider
{
Tenant GetFirstTenant();
Tenant GetTenantById(Guid id);
}
public class TenantProvider : ITenantProvider
{
private static readonly IList<Tenant> Tenants = new List<Tenant>
{
new Tenant
{
Id = Guid.Parse("51aab199-1482-4f0d-8ff1-5ca0e7bc525a"),Name = "Imaginary corp",DatabaseConnectionString = "server=localhost;port=3306;database=AuthServer;user=root;password=root"
},new Tenant
{
Id = Guid.Parse("ae4e21fa-57cb-4733-b971-fdd14c4c667e"),Name = "The Very Big corp",DatabaseConnectionString = "server=localhost;port=3306;database=AuthServer;user=root;password=root"
}
};
public Tenant GetFirstTenant()
{
return Tenants.First();
}
public Tenant GetTenantById(Guid id)
{
return Tenants.FirstOrDefault(t => t.Id == id);
}
}
在Startup.cs
中,为了添加AddIdentity
,我添加了一个连接字符串,但是该连接字符串必须来自TenantProvider
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppIdentityDbContext>(
options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection")
));
services.AddIdentity<AppUser,IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
// this adds the operational data from DB (codes,tokens,consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseMySql(Configuration.GetConnectionString("DefaultConnection"));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
})
//.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<AppUser>();
services.AddTransient<IProfileService,IdentityClaimsProfileService>();
services.AddScoped<IApplicationService,ApplicationService>();
services.AddCors(options => options.AddPolicy("AllowAll",p => p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()));
services.AddMvc(options =>
{
options.EnableEndpointRouting = false;
}).SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddTransient<ITenantProvider,TenantProvider>();
}
TenantMiddleware在applicationService中设置tenantId
,以便稍后使用tenantProvider注入
TenantMiddleware
public class TenantMiddleware
{
private readonly RequestDelegate next;
public TenantMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context,IApplicationService applicationService,ITenantProvider tenant)
{
var queryString = HttpUtility.ParseQueryString(context.Request.Query["ReturnUrl"]);
Guid.TryParse(queryString["tenant"],out var tenantId);
applicationService.TenantId = tenantId;
await next(context);
}
}
如果必须先等待Startup.cs
处理请求,如何在TenantMiddleware
中设置AppIdentityDbContext?能行吗?
解决方法
对于具有不同数据库源的多租户,无法在Startup.cs上进行设置,它必须在运行时(例如创建一些ContextFactory和其他用于处理DbContext设置的东西)运行。您可以尝试这篇文章,它可能会为您正在寻找的解决方案提供提示,Building Multi-tenant Web API With .NET Core and Best Practices。
,我认为IdentityServer4不支持该功能。为了实现您想做的事情,我假设您有一个“主”数据库来存储每个租户数据库的连接字符串,为什么不将用户也存储在数据库中?
对于我们的情况,我们将“ User”表分成2个。业务逻辑信息存储在Tenant DB中。与登录相关的信息存储在“ master”数据库中。并且我们添加了GUID列来链接这两个表。实际上,使用这两个表的唯一场景只有用户“激活”和“停用”,我们将从主服务中将其称为身份服务。除此之外,我们仅使用Tenant DB中的“用户”表。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。