MultitEnancyServer的目標是輕巧的軟件包,可輕鬆地為任何代碼庫添加多租賃支持。它的設計受到ASP.NET核心身份的嚴重影響。您可以在模型中添加多種租賃支持,而無需在任何類或實體中添加任何租戶密鑰屬性。使用ASP.NET核心,可以通過自定義域名,子域,部分主機名,HTTP請求標頭,兒童或部分URL路徑,查詢字符串參數,經過身份驗證的用戶索賠或自定義請求解析器實現來檢索當前租戶。使用實體框架核心,將租戶密鑰添加為陰影屬性(或可選的具體屬性)並通過全局查詢過濾器強制執行,所有可配置的選項都可以從默認值或每個實體中設置為超過。下面的示例突出顯示瞭如何將MultitEnancyserver與ASP.NET核心身份和身份證4一起使用。您可以找到許多與IndentityServer4,ASP.NET Core Identity(使用不同的關鍵類型(例如String和INT64))以及樣本repo中的實體框架核心集成的完整樣本。
定義您自己的房客模型,或從租賃者繼承,或者只是使用租賃者。在此示例中,我們將從租賃者繼承並添加顯示名稱。
public class Tenant : TenancyTenant
{
// Custom property for display name of tenant.
public string Name { get ; set ; }
} 向ASP.NET核心添加多租賃支持的示例。
public void ConfigureServices ( IServiceCollection services )
{
var connectionString = Configuration . GetConnectionString ( "DefaultConnection" ) ;
var migrationsAssembly = typeof ( AppDbContext ) . GetTypeInfo ( ) . Assembly . GetName ( ) . Name ;
services . AddDbContext < AppDbContext > ( options =>
{
options . UseSqlServer ( connectionString , sql => sql . MigrationsAssembly ( migrationsAssembly ) ) ;
} ) ;
// Add Multi-Tenancy Server defining TTenant<TKey> as type Tenant with an ID (key) of type string.
services . AddMultiTenancy < Tenant , string > ( )
// Add one or more IRequestParser (MultiTenancyServer.AspNetCore).
. AddRequestParsers ( parsers =>
{
// Parsers are processed in the order they are added,
// typically 1 or 2 parsers should be all you need.
parsers
// www.tenant1.com
. AddDomainParser ( )
// tenant1.tenants.multitenancyserver.io
. AddSubdomainParser ( ".tenants.multitenancyserver.io" )
// from partial hostname
. AddHostnameParser ( "^(regular_expression)$" )
// HTTP header X-TENANT = tenant1
. AddHeaderParser ( "X-TENANT" )
// /tenants/tenant1
. AddChildPathParser ( "/tenants/" )
// from partial path
. AddPathParser ( "^(regular_expression)$" )
// ?tenant=tenant1
. AddQueryParser ( "tenant" )
// Claim from authenticated user principal.
. AddClaimParser ( "http://schemas.microsoft.com/identity/claims/tenantid" )
// Add custom request parser with lambda.
. AddCustomParser ( httpContext => "tenant1" ) ;
// Add custom request parser implementation.
. AddMyCustomParser ( ) ;
} )
// Use in memory tenant store for development (MultiTenancyServer.Stores)
. AddInMemoryStore ( new Tenant [ ]
{
new Tenant ( )
{
Id = "TENANT_1" ,
CanonicalName = "Tenant1" ,
NormalizedCanonicalName = "TENANT1"
}
} )
// Use EF Core store for production (MultiTenancyServer.EntityFrameworkCore).
. AddEntityFrameworkStore < AppDbContext , Tenant , string > ( )
// Use custom store.
. AddMyCustomStore ( ) ;
// Add ASP.NET Core Identity
services . AddIdentity < User , Role > ( )
. AddEntityFrameworkStores < AppDbContext > ( )
. AddDefaultTokenProviders ( ) ;
// Add Identity Server 4
var builder = services . AddIdentityServer ( )
. AddAspNetIdentity < User > ( )
// Add the config data from DB (clients, resources)
. AddConfigurationStore < AppDbContext > ( options =>
{
options . ConfigureDbContext = b =>
b . UseSqlServer ( connectionString ,
sql => sql . MigrationsAssembly ( migrationsAssemblyName ) ) ;
} )
// Add the operational data from DB (codes, tokens, consents)
. AddOperationalStore < AppDbContext > ( options =>
{
options . ConfigureDbContext = b =>
b . UseSqlServer ( connectionString ,
sql => sql . MigrationsAssembly ( migrationsAssemblyName ) ) ;
} ) ;
if ( Environment . IsDevelopment ( ) )
{
builder . AddDeveloperSigningCredential ( ) ;
}
else
{
throw new Exception ( "Key not configured." ) ;
}
} 為ASP.NET Core MVC和IdentityServer4配置應用程序的示例。
public void Configure ( IApplicationBuilder app )
{
// other code removed for brevity
app . UseStaticFiles ( ) ;
app . UseMultiTenancy < Tenant > ( ) ;
app . UseIdentityServer ( ) ;
app . UseAuthentication ( ) ;
app . UseMvcWithDefaultRoute ( ) ;
} dbContext的示例具有多種租賃支持對ASP.NET核心身份和身份服務員4。
public class AppDbContext :
// ASP.NET Core Identity EF Core
IdentityDbContext < User , Role , string , UserClaim , UserRole , UserLogin , RoleClaim , UserToken > ,
// IdentityServer4 EF Core
IConfigurationDbContext , IPersistedGrantDbContext ,
// MultiTenancyServer EF Core
ITenantDbContext < Tenant , string >
{
private static object _tenancyModelState ;
private readonly ITenancyContext < Tenant > _tenancyContext ;
public AppDbContext (
DbContextOptions < AppDbContext > options ,
ITenancyContext < Tenant > tenancyContext )
: base ( options )
{
// The request scoped tenancy context.
// Should not access the tenancyContext.Tenant property in the constructor yet,
// as the request pipeline has not finished running yet and it will likely be null.
_tenancyContext = tenancyContext ;
}
// IdentityServer4 implementation.
public DbSet < Client > Clients { get ; set ; }
public DbSet < IdentityResource > IdentityResources { get ; set ; }
public DbSet < ApiResource > ApiResources { get ; set ; }
public DbSet < PersistedGrant > PersistedGrants { get ; set ; }
// MultiTenancyServer implementation.
public DbSet < Tenant > Tenants { get ; set ; }
protected override void OnModelCreating ( ModelBuilder builder )
{
base . OnModelCreating ( builder ) ;
// IdentityServer4 configuration.
var configurationStoreOptions = new ConfigurationStoreOptions ( ) ;
builder . ConfigureClientContext ( configurationStoreOptions ) ;
builder . ConfigureResourcesContext ( configurationStoreOptions ) ;
var operationalStoreOptions = new OperationalStoreOptions ( ) ;
builder . ConfigurePersistedGrantContext ( operationalStoreOptions ) ;
// MultiTenancyServer configuration.
var tenantStoreOptions = new TenantStoreOptions ( ) ;
builder . ConfigureTenantContext < Tenant , string > ( tenantStoreOptions ) ;
// Add multi-tenancy support to model.
var tenantReferenceOptions = new TenantReferenceOptions ( ) ;
builder . HasTenancy < string > ( tenantReferenceOptions , out _tenancyModelState ) ;
// Configure custom properties on Tenant (MultiTenancyServer).
builder . Entity < Tenant > ( b =>
{
b . Property ( t => t . Name ) . HasMaxLength ( 256 ) ;
} ) ;
// Configure properties on User (ASP.NET Core Identity).
builder . Entity < User > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenantId , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on NormalizedUserName.
b . HasIndex ( u => u . NormalizedUserName ) . HasName ( "UserNameIndex" ) . IsUnique ( false ) ;
// Add unique index on TenantId and NormalizedUserName.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( User . NormalizedUserName ) )
. HasName ( "TenantUserNameIndex" ) . IsUnique ( ) ;
} ) ;
// Configure properties on Role (ASP.NET Core Identity).
builder . Entity < Role > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on NormalizedName.
b . HasIndex ( r => r . NormalizedName ) . HasName ( "RoleNameIndex" ) . IsUnique ( false ) ;
// Add unique index on TenantId and NormalizedName.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( Role . NormalizedName ) )
. HasName ( "TenantRoleNameIndex" ) . IsUnique ( ) ;
} ) ;
// Configure properties on Client (IdentityServer4).
builder . Entity < Client > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on ClientId.
b . HasIndex ( c => c . ClientId ) . IsUnique ( false ) ;
// Add unique index on TenantId and ClientId.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( Client . ClientId ) ) . IsUnique ( ) ;
} ) ;
// Configure properties on IdentityResource (IdentityServer4).
builder . Entity < IdentityResource > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on Name.
b . HasIndex ( r => r . Name ) . IsUnique ( false ) ;
// Add unique index on TenantId and Name.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( IdentityResource . Name ) ) . IsUnique ( ) ;
} ) ;
// Configure properties on ApiResource (IdentityServer4).
builder . Entity < ApiResource > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on Name.
b . HasIndex ( r => r . Name ) . IsUnique ( false ) ;
// Add unique index on TenantId and Name.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( ApiResource . Name ) ) . IsUnique ( ) ;
} ) ;
// Configure properties on ApiScope (IdentityServer4).
builder . Entity < ApiScope > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState , hasIndex : false ) ;
// Remove unique index on Name.
b . HasIndex ( s => s . Name ) . IsUnique ( false ) ;
// Add unique index on TenantId and Name.
b . HasIndex ( tenantReferenceOptions . ReferenceName , nameof ( ApiScope . Name ) ) . IsUnique ( ) ;
} ) ;
// Configure properties on PersistedGrant (IdentityServer4).
builder . Entity < PersistedGrant > ( b =>
{
// Add multi-tenancy support to entity.
b . HasTenancy ( ( ) => _tenancyContext . Tenant . Id , _tenancyModelState ) ;
} ) ;
}
public override int SaveChanges ( bool acceptAllChangesOnSuccess )
{
// Ensure multi-tenancy for all tenantable entities.
this . EnsureTenancy ( _tenancyContext ? . Tenant ? . Id , _tenancyModelState , _logger ) ;
return base . SaveChanges ( acceptAllChangesOnSuccess ) ;
}
public override Task < int > SaveChangesAsync ( bool acceptAllChangesOnSuccess , CancellationToken cancellationToken = default )
{
// Ensure multi-tenancy for all tenantable entities.
this . EnsureTenancy ( _tenancyContext ? . Tenant ? . Id , _tenancyModelState , _logger ) ;
return base . SaveChangesAsync ( acceptAllChangesOnSuccess , cancellationToken ) ;
}
} public class TenantReferenceOptions
{
// Summary:
// If set to a non-null value, the store will use this value as the name for the
// tenant's reference property. The default is "TenantId".
public string ReferenceName { get ; set ; }
// Summary:
// True to enable indexing of tenant reference properties in the store, otherwise
// false. The default is true.
public bool IndexReferences { get ; set ; }
// Summary:
// If set to a non-null value, the store will use this value as the name of the
// index for any tenant references. The name is also a format pattern of {0:PropertyName}.
// The default is "{0}Index", eg. "TenantIdIndex".
public string IndexNameFormat { get ; set ; }
// Summary:
// Determines if a null tenant reference is allowed for entities and how querying
// for null tenant references is handled.
public NullTenantReferenceHandling NullTenantReferenceHandling { get ; set ; }
}
public enum NullTenantReferenceHandling
{
// Summary:
// A null tenant reference is NOT allowed for the entity, where possible a NOT NULL
// or REQUIRED constraint should be set on the tenant reference, querying for entities
// with a null tenant reference will match NO entities.
// This is the default option.
NotNullDenyAccess = 0 ,
// Summary:
// A null tenant reference is allowed for the entity, where possible an NULLABLE
// or OPTIONAL constraint should be set on the tenant reference, querying for entities
// with a null tenant reference will match those expected results.
// This may be useful where globally defined system entities are set with a null
// tenant reference.
NullableEntityAccess = 1 ,
// Summary:
// A null tenant reference is NOT allowed for the entity, where possible a NOT NULL
// or REQUIRED constraint should be set on the tenant reference, querying for entities
// with a null tenant reference will match ALL entities across all tenants.
// For obvious security reasons, this is typically not recommended; however, this
// can be useful for admin reporting across all tenants.
NotNullGlobalAccess = 2
} microsoft.aspnetcore.hosting.internal.webhost:信息:請求啟動http/1.1 post http:// localhost :5020/account/counce/login? returnUrl =%2fgrants application/x-www-form-urlcored 267
microsoft.entityframeworkcore.database.command:信息:執行dbCommand(3ms)[parameters = [@_____ normalizedcanonicalnicalname_0 =' localhost '(size = 256)],commandtype ='text',text',commandtime = '30',commandtimeout = '30' [u]。 [concurrencyStamp],[u]。 [name],[u]。 [normalizedCanonicalName] [pentants] as [u]其中[u]。
MultitEnancyServer.http.httptptenancyProvider:調試: domainparser在請求中http:// localhost中的localhost找到test_tenant_1 test_tenant_1:5020/account/local/login/login? returnUrl? returnUrl =%2fgrants。
Microsoft.EntityFrameWorkCore.Database.Command:信息:執行DBCommand(13ms)[parameters = [@__________________________________0 =' test_tenant_1 '(size = 4000), @_____ normalizedusizeduseizedUsizedUsizieduseName_0 ='alice ='alice'(size = 256)(size = 256) [u]。 [用戶名]來自[users] as [u]其中(@___ tenantid_0不是null和([u]。[tenantid] = @___ tenantid_0)))和([unormanizedusername] = @__ normalizedizedUseName_0)
Microsoft.EntityFrameWorkCore.DataBase.Command:信息:執行DBCommand(1MS)[parameters = [@____________________________________0 =' test_tenant_1 '(size = 4000),@__ objectiv_0 ='3AB999036-8AC1-8AC1-8AC1-4270-8AAC1-4270-8A1E = 200) commandType ='text',commandtimeout = '30']選擇[p]。 [鍵],[p]。 [clientId],[p]。 [creation time],[p],[p] 。 ([p]。[tenantid] = @___ tenantid_0))和([p]。