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

时间:2018-06-10 17:40:49

标签: validation asp.net-core-mvc asp.net-identity

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

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

CustomUserValidator.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));
            }
        }
    }
}

在Startup.cs中

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();
}

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

  1. 调试时,不会调用DuplicateUserName(),但仍会收到重复的用户名错误。
  2. 通过添加特殊字符对其他用户名验证进行测试。使用特殊字符验证失败两次错误!
  3. 我在这里做错了什么或错过了什么? 提前谢谢。

3 个答案:

答案 0 :(得分:2)

首先让我解释一下问题

原因是为什么身份库注入使用默认库UserValidator的验证用户类。

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

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

这是我的解决方案:

这是接口ICustomUserValidator

    public interface ICustomUserValidator<TUser> : IUserValidator<TUser> where TUser : ApplicationUser
{
}

和类的实现

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
    }
}

这是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)
    {
    }
}

请注意,我放了 ICustomUserValidator

,而不是IUserValidator。

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

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserManager<CustomUserManager<ApplicationUser>>()

最后注入这个类

            services.AddTransient<ICustomUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();

我希望该实现对您有所帮助。

答案 1 :(得分:0)

在注册身份之前替换默认的UserValidator

services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator<ApplicationUser>>();  // before Identity
services.AddDefaultIdentity<ApplicationUser>().AddRoles<Role>()
.AddEntityFrameworkStores<IdentityDbContext>()
.AddDefaultTokenProviders();

答案 2 :(得分:0)

Core 3.1中的语法略有不同:

services.AddTransient<IUserValidator<ApplicationUser>, CustomUserValidator>();

CustomUserValidator与标准用户验证器in the source here之间进行交互。如果要删除验证,这很有必要。请注意,某些验证(例如,用户名唯一性)受数据库约束的支持,因此您可能还需要对其进行调整。

如果您只想添加更多验证,则只需在startup.cs中将自己的验证器作为附加服务添加到Identity。