使用Identity Framework实现多租户的简单方法。如何?

时间:2017-09-13 18:27:42

标签: asp.net-web-api2 asp.net-identity asp.net-identity-2 asp.net-identity-3

我想在我的使用Identity Framework进行身份验证的web api应用程序中支持多租户。我打算有一个单个站点和一个包含多个用户的单个数据库。我很乐意为用户名添加租户ID作为登录用途,然后在将其返回到客户端应用程序时将其删除。 但是,我不确定如何“拦截”用户登录。也就是说,在之前,我将在哪里使用租户ID 为用户名添加前缀,然后将它们传递给框架进行身份验证。
有人可以让我知道如何处理上述问题。一个小例子和指向在线文档的指针将会被证实。

感谢。

1 个答案:

答案 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。