我想支持使用身份框架进行身份验证的Web API应用中的多租赁。我计划拥有一个单个站点和一个具有多个用户的租户的单个数据库。我很高兴将用户名带有租户ID以登录用途,然后在将其返回客户端应用程序时将其剥离。
但是,我不确定如何"拦截"用户登录。也就是说,我在哪里可以在之前使用租户ID 实现前缀的用户名。
有人可以让我知道如何处理以上内容。一个很小的例子和在线文档的指示将得到认可。
谢谢。
这是斯科特·布雷迪(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>());
}
}
然后您可以为每个租户创建Seprate用户经理:
user1 :
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。