为 .NET Core Identity 2.1 覆盖 UserValidator.cs 中的 ValidateAsync

Kes*_*Kes 4 validation asp.net-identity asp.net-core-mvc

我正在自定义用户名的验证以允许相同的用户名(非唯一)。这是带有附加字段“已删除”作为对身份用户的软删除。因此,自定义涉及更改当前验证以检查用户名是否已存在,并且已删除是否为 false 以仅触发 DuplicateUserName 错误。

我所做的是创建一个 CustomUserValidator 类,并覆盖 UserValidator.cs 中的 ValidateAsync 方法以及 ValidateUserName 方法。下面是代码:

自定义用户验证器.cs

public class CustomUserValidator<TUser> : UserValidator<TUser>
    where TUser : ApplicationUser
{
    public override async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
    {
        if (manager == null)
        {
            throw new ArgumentNullException(nameof(manager));
        }
        if (user == null)
        {
            throw new ArgumentNullException(nameof(user));
        }
        var errors = new List<IdentityError>();
        await ValidateUserName(manager, user, errors);
        if (manager.Options.User.RequireUniqueEmail)
        {
            await ValidateEmail(manager, user, errors);
        }
        return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
    }

    private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<IdentityError> errors)
    {
        var userName = await manager.GetUserNameAsync(user);
        if (string.IsNullOrWhiteSpace(userName))
        {
            errors.Add(Describer.InvalidUserName(userName));
        }
        else if (!string.IsNullOrEmpty(manager.Options.User.AllowedUserNameCharacters) &&
            userName.Any(c => !manager.Options.User.AllowedUserNameCharacters.Contains(c)))
        {
            errors.Add(Describer.InvalidUserName(userName));
        }
        else
        {
            //var owner = await manager.FindByNameAsync(userName);
            var owner = manager.Users.Where(x => !x.Deleted &&
                x.UserName.ToUpper() == userName.ToUpper())
                .FirstOrDefault();
            if (owner != null &&
                !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
            {
                errors.Add(Describer.DuplicateUserName(userName));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在 Startup.cs 中

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();
}
Run Code Online (Sandbox Code Playgroud)

CustomUserValidator 中的 ValidateAsync 方法中的代码工作正常,但原始 ValidateAsync 似乎也在运行。我之所以这么说是因为:

  1. 调试时,没有调用 DuplicateUserName(),但仍然收到重复的用户名错误。
  2. 通过放置特殊字符测试其他用户名验证。使用不允许的特殊字符验证失败两次!

我在这里做错了什么或错过了什么?提前致谢。

cca*_*sob 6

首先让我解释一下问题

原因就是为什么 Identity 库会注入使用默认库的验证用户类,即 UserValidator。

解决方案只是注入一个 CustomUserValidator。发生的情况是,如果它被注入到正常的实现中,它所做的是它添加了 2 个 UserValidator,第一个是身份库的默认值,第二个是您实现了 CustomUserIdentity 的那个。

然后只注入 CustomUserIdentity,您必须创建一个新的 CustomUserManager 才能注入新的 ICustomUserValidator,这样它就不会采用默认的 IUserValidator。

这是我的解决方案:

这是接口 ICustomUserValidator

    public interface ICustomUserValidator<TUser> : IUserValidator<TUser> where TUser : ApplicationUser
{
}
Run Code Online (Sandbox Code Playgroud)

和类的实现

public class CustomUserValidator<TUser> : UserValidator<TUser>, ICustomUserValidator<TUser>
    where TUser : ApplicationUser
{

    public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
    {
        //Some Code
    }

    private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<IdentityError> errors)
    {
        //Some Code
    }
}
Run Code Online (Sandbox Code Playgroud)

而这一个用于 CustomUserManager

    public class CustomUserManager<TUser> : UserManager<TUser>
            where TUser : ApplicationUser
{
    public CustomUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<TUser> passwordHasher, IEnumerable<ICustomUserValidator<TUser>> userValidators,
        IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IServiceProvider tokenProviders,
        ILogger<UserManager<TUser>> logger)
        : base(
            store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
            tokenProviders, logger)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我放置的不是 IUserValidator,而是 ICustomUserValidatorIUserValidator

在 Startup 类中,您必须注入新类:

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserManager<CustomUserManager<ApplicationUser>>()
Run Code Online (Sandbox Code Playgroud)

最后注入这个类

            services.AddTransient<ICustomUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();
Run Code Online (Sandbox Code Playgroud)

我希望这个实现可以帮助你。