为.NET Core Identity 2.1覆盖UserValidator.cs中的ValidateAsync [英] Override ValidateAsync in UserValidator.cs for .NET Core Identity 2.1

查看:36
本文介绍了为.NET Core Identity 2.1覆盖UserValidator.cs中的ValidateAsync的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在自定义用户名的验证,以允许使用相同的用户名(非唯一).这是一个附加字段已删除",作为对身份用户的软删除.因此,定制涉及更改当前的验证,以检查用户名是否已经存在并删除为false,从而仅触发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. 通过放置特殊字符来测试其他用户名验证.两次特殊字符验证失败,失败!

我在这里做错了什么或想念什么? 预先感谢.

解决方案

首先让我解释一下问题

原因是为什么身份库注入使用默认库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>>();

我希望这种实施方式对您有所帮助.

I'm customizing the validation for username to allow the same username (non-unique). This is with an additional field "Deleted" as a soft delete to identity user. So the customization involves changing the current validation to check if the username already exist and deleted is false to only trigger DuplicateUserName error.

What I've done is create a CustomUserValidator class, and override the ValidateAsync method in UserValidator.cs as well as the ValidateUserName method. Below is the code:

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

And in Startup.cs

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

The code in ValidateAsync method in CustomUserValidator works fine, but it seems the original ValidateAsync is running as well. Why I say so is because:

  1. While debugging, DuplicateUserName() is not called, but still receiving duplicate username error.
  2. Tested for other username validation by putting special characters. Failed validation with the special characters not allowed error twice!

What am I doing wrong or missing here? Thanks in advance.

解决方案

First let me explain the problem

The reason is why the Identity library injects the validate user class that uses the default library, which is UserValidator.

The solution is only to inject a CustomUserValidator. What happens is that if it is injected into the normal implementation, what it does is that it adds 2 UserValidator, the first is the default by the identity library and the second would be the one that you implemented the CustomUserIdentity.

Then to inject only the CustomUserIdentity you must create a new CustomUserManager to be able to inject the new ICustomUserValidator so that it does not take the one that is by default IUserValidator.

This is my solution:

This is the interface ICustomUserValidator

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

And the implementation of the class

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

And this one for the 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)
    {
    }
}

Notice that instead of IUserValidator, I put ICustomUserValidator

In the Startup class you have to inject the new class:

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

And finally inject this class

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

I hope this implementation helps you.

这篇关于为.NET Core Identity 2.1覆盖UserValidator.cs中的ValidateAsync的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆