UserClaimsPrincipalFactory выдает исключение при входе в систему - PullRequest
0 голосов
/ 07 октября 2019

Мне нужно реализовать Identity Core с первичным ключом типа int и пользовательской схемой User. Кроме того, мне нужно реализовать несколько пользовательских полей в IdentityUser и Role. После того, как я все сделал и попытался использовать

 await _signInManager.PasswordSignInAsync(user, vm.Password, isPersistent: false, false);

, я получаю следующее исключение, которое я не могу отладить из-за любви к нему:

System.ArgumentNullException: Value cannot be null.
Parameter name: value
   at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
   at System.Security.Claims.Claim..ctor(String type, String value)
   at Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory`1.GenerateClaimsAsync(TUser user)
   at Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory`2.GenerateClaimsAsync(TUser user)
   at Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory`1.CreateAsync(TUser user)
   at Microsoft.AspNetCore.Identity.SignInManager`1.CreateUserPrincipalAsync(TUser user)
   at Microsoft.AspNetCore.Identity.SignInManager`1.SignInAsync(TUser user, AuthenticationProperties authenticationProperties, String authenticationMethod)
   at Microsoft.AspNetCore.Identity.SignInManager`1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor)
   at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(TUser user, String password, Boolean isPersistent, Boolean lockoutOnFailure)

Да, часть значенияконструктора Claim является нулевым. Мне нужна помощь в выяснении, почему это происходит, поскольку я не могу войти в эту часть кода.

Давайте начнем с Конфигурации идентификации:

services.AddIdentity<SpotlightUser, SpotlightRole>()
        .AddEntityFrameworkStores<SpotlightContext>()
        .AddDefaultTokenProviders();

Контекст:

public class SpotlightContext : IdentityDbContext<
    SpotlightUser, SpotlightRole, int,
    SpotlightUserClaim, SpotlightUserRole, SpotlightUserLogin,
    SpotlightRoleClaim, SpotlightUserToken>
{
    public SpotlightContext(DbContextOptions<SpotlightContext> options)
        :base(options)
    {

    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        //inject custom model creation options here
        builder.ApplyConfiguration(new SpotlightUserConfiguration());
        builder.ApplyConfiguration(new SpotlightRoleConfiguration());
        /*...omitted*/
    }

    public override int SaveChanges()
    {
        ApplyShadowProperties();
        return base.SaveChanges();
    }

    /*...omitted*/
}

Вот реализации пользователя и роли:

public class SpotlightUser : IdentityUser<int>, ITrackable
{
    public DateTime CreatedDate { get; set; }
    public DateTime ModifiedDate { get; set; }

    public virtual ICollection<SpotlightUserClaim> Claims { get; set; }
    public virtual ICollection<SpotlightUserLogin> Logins { get; set; }
    public virtual ICollection<SpotlightUserToken> Tokens { get; set; }
    public virtual ICollection<SpotlightUserRole> UserRoles { get; set; }
}

public class SpotlightRole : IdentityRole<int>, ITrackable
{
    public DateTime CreatedDate { get; set; }
    public DateTime ModifiedDate { get; set; }

    public virtual ICollection<SpotlightUserRole> UserRoles { get; set; }
    public virtual ICollection<SpotlightRoleClaim> RoleClaims { get; set; }
}

ITrackable только определяет, что поля Created и Modified должны присутствовать.

Точно так же я определил всеклассы спутников:

public class SpotlightRoleClaim : IdentityRoleClaim<int>
{
    public virtual SpotlightRole Role { get; set; }
}

Наконец, вот конфигурация для SpotlightUser:

public class SpotlightUserConfiguration : IEntityTypeConfiguration<SpotlightUser>
{
    public void Configure(EntityTypeBuilder<SpotlightUser> builder)
    {
        builder.ToTable("AspNetUsers", schema: "User");
        builder.Property(x => x.Company).HasMaxLength(200);
        builder.Property(x => x.IsActive).HasDefaultValue(true);
        builder.Property(x => x.IsAdmin).HasDefaultValue(false);

        builder.HasMany(x => x.Claims)
            .WithOne()
            .HasForeignKey(uc => uc.UserId)
            .IsRequired();

        builder.HasMany(x => x.Logins)
            .WithOne()
            .HasForeignKey(l => l.UserId)
            .IsRequired();

        builder.HasMany(e => e.Tokens)
            .WithOne()
            .HasForeignKey(ut => ut.UserId)
            .IsRequired();

        builder.HasMany(e => e.UserRoles)
            .WithOne(e => e.User)
            .HasForeignKey(ur => ur.UserId)
            .IsRequired();
    }
}

Как вы можете видеть эти пузырьки до GenerateClaimsAsync, и я подозреваю, что они разбиваются вэти строки (https://github.com/aspnet/Identity/blob/master/src/Core/UserClaimsPrincipalFactory.cs)

id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
    id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
    await UserManager.GetSecurityStampAsync(user)));
}
if (UserManager.SupportsUserClaim)
{
     id.AddClaims(await UserManager.GetClaimsAsync(user));
}

Почему это происходит и какую часть я упустил из виду в своей реализации? Я следовал этой статье для руководства: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-2.2#customize-the-model

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...