【问题标题】:Duplicate Role Names on Asp.Net Identity and Multi-tenancyAsp.Net 身份和多租户上的重复角色名称
【发布时间】:2017-06-07 14:30:00
【问题描述】:

我正在使用 ASP.Net MVC 和 Identity 2.0 开发多租户 Web 应用程序。我已经像这样扩展了 IdentityRole:

public class ApplicationRole : IdentityRole
{
    public ApplicationRole() : base() { }
    public ApplicationRole(string name) : base(name) { }

    public string TenantId { get; set; }
}

这是因为每个租户都有单独的角色集,例如“管理员”、“员工”等。

但问题是,当我添加新角色时,如果“租户 A”具有“管理员”角色,当我向“租户 B”添加“管理员”角色时,我会收到 IdentityResult 错误,因为“管理员”名字被取了……这有点明显,因为 AspNetRoles 表上的“名称”字段是唯一的……

IdentityResult roleResult = await RoleManager.CreateAsync(
  new ApplicationRole
  {
    Name = "Admin",
    TenantId = GetTenantId()
  });

但是我如何自定义 ASP.Net 身份,以便“AspNetRoles”中的“名称”字段可以与“TenantId”是唯一的,而不是单独的?我找到了有关扩展 IdentityRole 的信息(就像我添加了一个字段一样),但没有找到关于更改或替换它的信息...

【问题讨论】:

    标签: asp.net-mvc multi-tenant asp.net-identity-2


    【解决方案1】:

    只需在ApplicationDbContextOnModelCreating 方法上更改数据库的架构,如下所示:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        var role = modelBuilder.Entity<IdentityRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(
                new IndexAttribute("RoleNameIndex") 
                { IsUnique = false }));
    }
    

    但您还必须自定义 RoleValidator 类。因为默认角色验证器会使重复的角色名称无效。

    public class MyRoleValidator:RoleValidator<ApplicationRole>
    {
         public override async Task<IdentityResult> ValidateAsync(ApplicationRole item)
         {
             // implement your validation logic here
    
             return IdentityResult.Success;
         }
    }
    

    现在每次创建角色管理器时都必须设置角色验证器。

    roleManager.RoleValidator=new MyRoleValidator();
    

    【讨论】:

    • Sam,当我尝试您的解决方案时,add-migration 命令会引发此错误:为表 'IdentityRole' 上的列 'Name' 指定了冲突的配置设置:索引属性属性 'IsUnique' = 'False ' 与索引属性属性 'IsUnique' = 'True' 冲突
    • 但之后抛出异常:一个或多个实体的验证失败。有关详细信息,请参阅“EntityValidationErrors”属性。
    • @ClickOk 你是怎么解决这个问题的?您说它会引发错误,但您将此解决方案标记为已接受,它真的有效吗?
    • @ibubi,对不起,我不记得了,这个答案是大约 3 年前的一个旧项目。但是这条消息是在我发表评论后编辑的,所以这个编辑可能解决了问题。
    【解决方案2】:

    您可以在 RoleName 和 TenantId 上添加复合唯一约束,但首先必须删除角色名称的唯一约束,这里是代码:

     public class ApplicationRoleConfiguration : IEntityTypeConfiguration<ApplicationRole>
    {
        public void Configure(EntityTypeBuilder<ApplicationRole> builder)
        {
            //remove the current idenx
            builder.HasIndex(x => x.NormalizedName).IsUnique(false);
            // add composite constraint 
            builder.HasIndex(x => new { x.NormalizedName, x.TenantId }).IsUnique();
        }
    }
    

    然后必须覆盖角色验证器以检查角色名称和 TenantId 的唯一性:

    public class TenantRoleValidator : RoleValidator<ApplicationRole>
    {
        private IdentityErrorDescriber Describer { get; set; }
    
        public TenantRoleValidator() : base()
        {
    
        }
        public override async Task<IdentityResult> ValidateAsync(RoleManager<ApplicationRole> manager, ApplicationRole role)
        {
            if (manager == null)
            {
                throw new ArgumentNullException(nameof(manager));
            }
            if (role == null)
            {
                throw new ArgumentNullException(nameof(role));
            }
            var errors = new List<IdentityError>();
            await ValidateRoleName(manager, role, errors);
            if (errors.Count > 0)
            {
                return IdentityResult.Failed(errors.ToArray());
            }
            return IdentityResult.Success;
        }
        private async Task ValidateRoleName(RoleManager<ApplicationRole> manager, ApplicationRole role,
        ICollection<IdentityError> errors)
        {
            var roleName = await manager.GetRoleNameAsync(role);
            if (string.IsNullOrWhiteSpace(roleName))
            {
                errors.Add(Describer.InvalidRoleName(roleName));
            }
            else
            {
                var owner = await manager.FindByNameAsync(roleName);
                if (owner != null
                    && owner.TenantId == role.TenantId
                    && !string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role)))
                {
                    errors.Add(Describer.DuplicateRoleName(roleName));
                }
            }
        }
    }
    

    最后,注册新的角色验证器:

            services
              .AddIdentityCore<ApplicationUser>()
              .AddRoles<ApplicationRole>()
              .AddRoleValidator<TenantRoleValidator>()
    

    在运行代码之前不要忘记将更改迁移到数据库

    【讨论】:

      猜你喜欢
      • 2017-07-25
      • 2017-03-13
      • 2014-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-05
      相关资源
      最近更新 更多