我想在我的使用Identity Framework进行身份验证的web api应用程序中支持多租户。我打算有一个单个站点和一个包含多个用户的单个数据库。我很乐意为用户名添加租户ID作为登录用途,然后在将其返回到客户端应用程序时将其删除。
但是,我不确定如何“拦截”用户登录。也就是说,在之前,我将在哪里使用租户ID 为用户名添加前缀,然后将它们传递给框架进行身份验证。
有人可以让我知道如何处理上述问题。一个小例子和指向在线文档的指针将会被证实。
感谢。
答案 0 :(得分:1)
Here Scott Brady有一个解决方案,你可以在那里阅读解释我只是在这里编写代码并进行一些更新。
解决方案:
向您的用户添加新属性以确定租户。
public class ApplicationUser : IdentityUser {
public int TenantId { get; set; }
}
然后,您需要自定义UserStore
以使其了解您的新用户属性。
public class ApplicationUserStore<TUser> : UserStore<TUser>
where TUser : ApplicationUser {
public ApplicationUserStore(DbContext context, int tenantId)
: base(context) {
TenantId = tenantId
}
public int TenantId { get; set; }
public override Task CreateAsync(TUser user) {
if (user == null) {
throw new ArgumentNullException("user");
}
user.TenantId = this.TenantId;
return base.CreateAsync(user);
}
public override Task<TUser> FindByEmailAsync(string email) {
return this.GetUserAggregateAsync(u => u.Email.ToUpper() == email.ToUpper()
&& u.TenantId == this.TenantId);
}
public override Task<TUser> FindByNameAsync(string userName) {
return this.GetUserAggregateAsync(u => u.UserName.ToUpper() == userName.ToUpper()
&& u.TenantId == this.TenantId);
}
public override Task<IdnUser> FindByIdAsync(long userId)
{
return this.GetUserAggregateAsync(u => u.Id == userId && u.TenantId == this.tenantId);
}
}
您还需要自定义IdentityDbContext
以支持多租户。
public class ApplicationUserDbContext<TUser> : IdentityDbContext<TUser>
where TUser : ApplicationUser {
public ApplicationUserDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var user = modelBuilder.Entity<TUser>();
user.Property(u => u.UserName)
.IsRequired()
.HasMaxLength(256)
.HasColumnAnnotation("Index", new IndexAnnotation(
new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 1}));
user.Property(u => u.TenantId)
.IsRequired()
.HasColumnAnnotation("Index", new IndexAnnotation(
new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 2 }));
}
protected override DbEntityValidationResult ValidateEntity(
DbEntityEntry entityEntry, IDictionary<object, object> items) {
if (entityEntry != null && entityEntry.State == EntityState.Added) {
var errors = new List<DbValidationError>();
var user = entityEntry.Entity as TUser;
if (user != null) {
if (this.Users.Any(u => string.Equals(u.UserName, user.UserName)
&& u.TenantId == user.TenantId)) {
errors.Add(new DbValidationError("User",
string.Format("Username {0} is already taken for AppId {1}",
user.UserName, user.TenantId)));
}
if (this.RequireUniqueEmail
&& this.Users.Any(u => string.Equals(u.Email, user.Email)
&& u.TenantId == user.TenantId)) {
errors.Add(new DbValidationError("User",
string.Format("Email Address {0} is already taken for AppId {1}",
user.UserName, user.TenantId)));
}
}
else {
var role = entityEntry.Entity as IdentityRole;
if (role != null && this.Roles.Any(r => string.Equals(r.Name, role.Name))) {
errors.Add(new DbValidationError("Role",
string.Format("Role {0} already exists", role.Name)));
}
}
if (errors.Any()) {
return new DbEntityValidationResult(entityEntry, errors);
}
}
return new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
}
}
然后您可以为每个租户创建单独的用户管理器,如下所示:
<强>用户1 强>:
public class AppCustomerManager : UserManager<ApplicationUser>
{
public AppCustomerManager(IUserStore<ApplicationUser> store)
: base(store)
{
UserTokenProvider = new PhoneNumberTokenProvider<ApplicationUser >();
}
public static AppCustomerManager Create(IdentityFactoryOptions<AppCustomerManager> options, IOwinContext context)
{
var appDbContext = context.Get<ApplicationUserDbContext>();
//here you define your tenant by passing an int to userstore
var appUserManager = new AppCustomerManager(new ApplicationUserStore(appDbContext, 1));
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
appUserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6)
};
}
return appUserManager;
}
}
<强>用户2 强>:
public class AppDeliveryManager : UserManager<ApplicationUser>
{
public AppDeliveryManager(IUserStore<ApplicationUser> store)
: base(store)
{
UserTokenProvider = new PhoneNumberTokenProvider<ApplicationUser >();
}
public static AppDeliveryManager Create(IdentityFactoryOptions<AppDeliveryManager> options, IOwinContext context)
{
var appDbContext = context.Get<IdnDbContext>();
//here you define your tenant by passing an int to userstore
var appUserManager = new AppDeliveryManager(new ApplicationUserStore(appDbContext, 2));
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
appUserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6)
};
}
return appUserManager;
}
}
<强>更新强>
var appUserManager = new AppDeliveryManager(new ApplicationUserStore(appDbContext, tenantId));
和tenantId可以是注册的每个租户的ID。