定义
软件多租户是一个软件架构,软件只有一个实例运行在服务器上,并服务于多个租户。一个租户包含一组用户,他们拥有指定权限,共同访问一个软件示例。一个多租户架构,应用程序未每个租户提供一个专属于他们的数据、配置、用户管理、租户特有的功能和属性。多租户通常用来创建Saas应用。
ABP多租户类库
基本定义与核心逻辑存放在Volo.ABP.MultiTenancy 内部。
针对ASP.NET Core MVC的集成则是由Volo.ABP.AspNetCore.MultiTenancy 项目实现的,针对多租户的 解析 都在这个项目内部。
租户数据的存储和管理都由Volo.ABP.TenantManagement 模块提供。
ABP 类图
ABP 多租户体现
修改示例项目代码:
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled; // IsEnabled设为True;
});
如此便启用了多租户,新建租户:
切换租户:
查看Cookie,可以看到有一个租户标记:
剖析:
Volo.Abp.TenantManagement 项目:提供一个UI界面,实现了一个租户的CRUD操作。
Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy 项目:提供了一个简单地UI界面,实现了切换租户。
在切换租户保存时,会将租户的ID写入进Cookie里面:
public async Task OnPostAsync()
{
if (Input.Name.IsNullOrEmpty())
{
Response.Cookies.Delete(Options.TenantKey);
}
else
{
var tenant = await TenantStore.FindAsync(Input.Name);
if (tenant == null)
{
throw new UserFriendlyException(L["GivenTenantIsNotAvailable", Input.Name]);
}
Response.Cookies.Append(
Options.TenantKey, // TenantKey = TenantResolverConsts.DefaultTenantKey; string DefaultTenantKey = "__tenant";
tenant.Id.ToString(),
new CookieOptions
{
Path = "/",
HttpOnly = false,
Expires = DateTimeOffset.Now.AddYears(10)
}
);
}
}
多租户应用模块
ABP中有一个默认的多租户模块 Volo.Abp.TenantManagement 项目。可以再Module文件夹中查看到该项目的源码,但是要在其他的项目中引入多租户的支持,需要实现数据种子。因为在该项目的源码有这么一个类
public class AbpTenantManagementWebMainMenuContributor : IMenuContributor
{
public virtual async Task ConfigureMenuAsync(MenuConfigurationContext context)
{
if (context.Menu.Name != StandardMenus.Main)
{
return;
}
var administrationMenu = context.Menu.GetAdministration();
var l = context.GetLocalizer<AbpTenantManagementResource>();
var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users");
administrationMenu.AddItem(tenantManagementMenuItem);
// 如果有权限,那么将会显示租户模块
if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
{
tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "~/TenantManagement/Tenants"));
}
}
}
而权限的配置有一个单独的类来处理
public class AbpTenantManagementPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var tenantManagementGroup = context.AddGroup(TenantManagementPermissions.GroupName, L("Permission:TenantManagement"));
var tenantsPermission = tenantManagementGroup.AddPermission(TenantManagementPermissions.Tenants.Default, L("Permission:TenantManagement"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(TenantManagementPermissions.Tenants.Create, L("Permission:Create"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(TenantManagementPermissions.Tenants.Update, L("Permission:Edit"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(TenantManagementPermissions.Tenants.Delete, L("Permission:Delete"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(TenantManagementPermissions.Tenants.ManageFeatures, L("Permission:ManageFeatures"), multiTenancySide: MultiTenancySides.Host);
tenantsPermission.AddChild(TenantManagementPermissions.Tenants.ManageConnectionStrings, L("Permission:ManageConnectionStrings"), multiTenancySide: MultiTenancySides.Host);
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<AbpTenantManagementResource>(name);
}
}
PermissionDefinitionProvider 类的处理则是在 Volo.Abp.Authorization 项目中,由PermissionDefinitionManager 来处c#教程理授权 Permissions。
在项目 Volo.Abp.PermissionManagement 中处理授权Permissions 的数据种子
public class PermissionDataSeedContributor : IDataSeedContributor,
ITransientDependency
{
protected ICurrentTenant CurrentTenant { get; }
protected IPermissionDefinitionManager PermissionDefinitionManager { get; }
protected IPermissionDataSeeder PermissionDataSeeder { get; }
public PermissionDataSeedContributor(
IPermissionDefinitionManager permissionDefinitionManager,
IPermissionDataSeeder permissionDataSeeder,
ICurrentTenant currentTenant)
{
PermissionDefinitionManager = permissionDefinitionManager;
PermissionDataSeeder = permissionDataSeeder;
CurrentTenant = currentTenant;
}
public virtual Task SeedAsync(DataSeedContext context)
{
var multiTenancySide = CurrentTenant.GetMultiTenancySide();
var permissionNames = PermissionDefinitionManager
.GetPermissions()
.Where(p => p.MultiTenancySide.HasFlag(multiTenancySide))
.Select(p => p.Name)
.ToArray();
return PermissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissionNames,
context.TenantId
);
}
}
因此,如果要集成多租户应用,只需要在XXXWebModule中生成python基础教程数据种子即可
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
// 其它code....
using (var scope = context.ServiceProvider.CreateScope())
{
AsyncHelper.RunSync(async () =>
{
await scope.ServiceProvider
.GetRequiredService<IDataSeeder>()
.SeedAsync();
});
}
}
由此便处理了授权,才能应用到多租户。