Asp.net Identity 2.0 extend UserValidator with custom unique property

前端 未结 1 719
难免孤独
难免孤独 2021-01-18 20:53

I would like to extend UserValidator or something similar in Asp.net Identity 2.0 to not only check for unique email but also a unique value of my choosing. Example of what

1条回答
  •  醉酒成梦
    2021-01-18 21:18

    A bit tricky to do but possible, needs a custom UserValidator. Looking at Microsoft.AspNet.Identity.Core source really helped me.

    https://aspnetidentity.codeplex.com/SourceControl/latest#src/Microsoft.AspNet.Identity.Core/UserValidator.cs

    CustomUserValidator, removed some comments due to body character limitation:

    public class CustomUserValidator : UserValidator
        where TUser : ApplicationUser
    {
        public bool RequireUniqueAlias { get; set; }
    
        public CustomUserValidator(UserManager manager) : base(manager)
        {
            this.Manager = manager;
        }
    
        private UserManager Manager { get; set; }
    
        public override async Task ValidateAsync(TUser item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            var errors = new List();
            await ValidateUserName(item, errors);
            if (RequireUniqueEmail)
            {
                await ValidateEmail(item, errors);
            }
            if (RequireUniqueAlias)
            {
                await ValidateAlias(item, errors);
            }
            if (errors.Count > 0)
            {
                return IdentityResult.Failed(errors.ToArray());
            }
            return IdentityResult.Success;
        }
    
        private async Task ValidateUserName(TUser user, List errors)
        {
            if (string.IsNullOrWhiteSpace(user.UserName))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.PropertyTooShort, "Name"));
            }
            else if (AllowOnlyAlphanumericUserNames && !Regex.IsMatch(user.UserName, @"^[A-Za-z0-9@_\.]+$"))
            {
                // If any characters are not letters or digits, its an illegal user name
                errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.InvalidUserName, user.UserName));
            }
            else
            {
                var owner = await Manager.FindByNameAsync(user.UserName);
                if (owner != null && !EqualityComparer.Default.Equals(owner.Id, user.Id))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.DuplicateName, user.UserName));
                }
            }
        }
    
        // make sure email is not empty, valid, and unique
        private async Task ValidateEmail(TUser user, List errors)
        {
            if (!user.Email.IsNullOrWhiteSpace())
            {
                if (string.IsNullOrWhiteSpace(user.Email))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.PropertyTooShort, "Email"));
                    return;
                }
                try
                {
                    var m = new MailAddress(user.Email);
                }
                catch (FormatException)
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.InvalidEmail, user.Email));
                    return;
                }
            }
            var owner = await Manager.FindByEmailAsync(user.Email);
            if (owner != null && !EqualityComparer.Default.Equals(owner.Id, user.Id))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.DuplicateEmail, user.Email));
            }
        }
    
        private async Task ValidateAlias(TUser user, List errors)
        {
            if (!user.Alias.IsNullOrWhiteSpace())
            {
                if (string.IsNullOrWhiteSpace(user.Alias))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.PropertyTooShort, "Alias"));
                    return;
                }
            }
            var owner = Manager.Users.FirstOrDefault(x => x.Alias == user.Alias);
            if (owner != null && !EqualityComparer.Default.Equals(owner.Id, user.Id))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, CustomResources.DuplicateAlias, user.Alias));
            }
        }
    }
    

    Apart from small modifications you also need to add a custom resource file, this is really useful as well if you wish to customize Asp.net Identity 2.0 username already taken validation message or other messages. Add a resource file, for example CustomResources.resx. Open folder in file explorer and edit CustomResource.resx in notepad or similar. Replace with the following data:

    
    
      
      
        
        
          
            
              
                
                  
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                  
                
              
              
                
                  
                    
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                    
                  
                  
                
              
            
          
        
      
      
        text/microsoft-resx
      
      
        2.0
      
      
        System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        An unknown failure has occured.
        Default identity result error message
      
      
        Email '{0}' is already taken.
        error for duplicate emails
      
      
        Name {0} is already taken.
        error for duplicate usernames
      
      
        A user with that external login already exists.
        Error when a login already linked
      
      
        Email '{0}' is invalid.
        invalid email
      
      
        Invalid token.
        Error when a token is not recognized
      
      
        User name {0} is invalid, can only contain letters or digits.
        usernames can only contain letters or digits
      
      
        Lockout is not enabled for this user.
        error when lockout is not enabled
      
      
        No IUserTokenProvider is registered.
        Error when there is no IUserTokenProvider
      
      
        No IUserTwoFactorProvider for '{0}' is registered.
        Error when there is no provider found
      
      
        Incorrect password.
        Error when a password doesn't match
      
      
        Passwords must have at least one digit ('0'-'9').
        Error when passwords do not have a digit
      
      
        Passwords must have at least one lowercase ('a'-'z').
        Error when passwords do not have a lowercase letter
      
      
        Passwords must have at least one non letter or digit character.
        Error when password does not have enough letter or digit characters
      
      
        Passwords must have at least one uppercase ('A'-'Z').
        Error when passwords do not have an uppercase letter
      
      
        Passwords must be at least {0} characters.
        Error message for passwords that are too short
      
      
        {0} cannot be null or empty.
        error for empty or null usernames
      
      
        Role {0} does not exist.
        error when a role does not exist
      
      
        Store does not implement IQueryableRoleStore<TRole>.
        error when the store does not implement this interface
      
      
        Store does not implement IQueryableUserStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserClaimStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserConfirmationStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserEmailStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserLockoutStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserLoginStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserPasswordStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserPhoneNumberStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserRoleStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserSecurityStampStore<TUser>.
        error when the store does not implement this interface
      
      
        Store does not implement IUserTwoFactorStore<TUser>.
        error when the store does not implement this interface
      
      
        User already has a password set.
        error when AddPassword called when a user already has a password
      
      
        User already in role.
        Error when a user is already in a role
      
      
        UserId not found.
        No user with this id found
      
      
        User {0} does not exist.
        error when a user does not exist
      
      
        User is not in role.
        Error when a user is not in the role
      
    
    

    Open CustomResources.resx and add your property, in my case "DuplicateAlias" as name. Now open CustomResource.Designer and add the following methods below internal static global::System.Globalization.CultureInfo Culture

    /// 
    ///   Looks up a localized string similar to An unknown failure has occured..
    /// 
    internal static string DefaultError {
        get {
            return ResourceManager.GetString("DefaultError", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Email '{0}' is already taken..
    /// 
    internal static string DuplicateEmail {
        get {
            return ResourceManager.GetString("DuplicateEmail", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Name {0} is already taken..
    /// 
    internal static string DuplicateName {
        get {
            return ResourceManager.GetString("DuplicateName", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Alias {0} is already taken..
    /// 
    internal static string DuplicateAlias {
        get {
            return ResourceManager.GetString("DuplicateAlias", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to A user with that external login already exists..
    /// 
    internal static string ExternalLoginExists {
        get {
            return ResourceManager.GetString("ExternalLoginExists", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Email '{0}' is invalid..
    /// 
    internal static string InvalidEmail {
        get {
            return ResourceManager.GetString("InvalidEmail", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Invalid token..
    /// 
    internal static string InvalidToken {
        get {
            return ResourceManager.GetString("InvalidToken", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to User name {0} is invalid, can only contain letters or digits..
    /// 
    internal static string InvalidUserName {
        get {
            return ResourceManager.GetString("InvalidUserName", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Lockout is not enabled for this user..
    /// 
    internal static string LockoutNotEnabled {
        get {
            return ResourceManager.GetString("LockoutNotEnabled", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to No IUserTokenProvider is registered..
    /// 
    internal static string NoTokenProvider {
        get {
            return ResourceManager.GetString("NoTokenProvider", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to No IUserTwoFactorProvider for '{0}' is registered..
    /// 
    internal static string NoTwoFactorProvider {
        get {
            return ResourceManager.GetString("NoTwoFactorProvider", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Incorrect password..
    /// 
    internal static string PasswordMismatch {
        get {
            return ResourceManager.GetString("PasswordMismatch", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Passwords must have at least one digit ('0'-'9')..
    /// 
    internal static string PasswordRequireDigit {
        get {
            return ResourceManager.GetString("PasswordRequireDigit", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Passwords must have at least one lowercase ('a'-'z')..
    /// 
    internal static string PasswordRequireLower {
        get {
            return ResourceManager.GetString("PasswordRequireLower", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Passwords must have at least one non letter or digit character..
    /// 
    internal static string PasswordRequireNonLetterOrDigit {
        get {
            return ResourceManager.GetString("PasswordRequireNonLetterOrDigit", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Passwords must have at least one uppercase ('A'-'Z')..
    /// 
    internal static string PasswordRequireUpper {
        get {
            return ResourceManager.GetString("PasswordRequireUpper", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Passwords must be at least {0} characters..
    /// 
    internal static string PasswordTooShort {
        get {
            return ResourceManager.GetString("PasswordTooShort", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to {0} cannot be null or empty..
    /// 
    internal static string PropertyTooShort {
        get {
            return ResourceManager.GetString("PropertyTooShort", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Role {0} does not exist..
    /// 
    internal static string RoleNotFound {
        get {
            return ResourceManager.GetString("RoleNotFound", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IQueryableRoleStore<TRole>..
    /// 
    internal static string StoreNotIQueryableRoleStore {
        get {
            return ResourceManager.GetString("StoreNotIQueryableRoleStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IQueryableUserStore<TUser>..
    /// 
    internal static string StoreNotIQueryableUserStore {
        get {
            return ResourceManager.GetString("StoreNotIQueryableUserStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserClaimStore<TUser>..
    /// 
    internal static string StoreNotIUserClaimStore {
        get {
            return ResourceManager.GetString("StoreNotIUserClaimStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserConfirmationStore<TUser>..
    /// 
    internal static string StoreNotIUserConfirmationStore {
        get {
            return ResourceManager.GetString("StoreNotIUserConfirmationStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserEmailStore<TUser>..
    /// 
    internal static string StoreNotIUserEmailStore {
        get {
            return ResourceManager.GetString("StoreNotIUserEmailStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserLockoutStore<TUser>..
    /// 
    internal static string StoreNotIUserLockoutStore {
        get {
            return ResourceManager.GetString("StoreNotIUserLockoutStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserLoginStore<TUser>..
    /// 
    internal static string StoreNotIUserLoginStore {
        get {
            return ResourceManager.GetString("StoreNotIUserLoginStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserPasswordStore<TUser>..
    /// 
    internal static string StoreNotIUserPasswordStore {
        get {
            return ResourceManager.GetString("StoreNotIUserPasswordStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserPhoneNumberStore<TUser>..
    /// 
    internal static string StoreNotIUserPhoneNumberStore {
        get {
            return ResourceManager.GetString("StoreNotIUserPhoneNumberStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserRoleStore<TUser>..
    /// 
    internal static string StoreNotIUserRoleStore {
        get {
            return ResourceManager.GetString("StoreNotIUserRoleStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserSecurityStampStore<TUser>..
    /// 
    internal static string StoreNotIUserSecurityStampStore {
        get {
            return ResourceManager.GetString("StoreNotIUserSecurityStampStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to Store does not implement IUserTwoFactorStore<TUser>..
    /// 
    internal static string StoreNotIUserTwoFactorStore {
        get {
            return ResourceManager.GetString("StoreNotIUserTwoFactorStore", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to User already has a password set..
    /// 
    internal static string UserAlreadyHasPassword {
        get {
            return ResourceManager.GetString("UserAlreadyHasPassword", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to User already in role..
    /// 
    internal static string UserAlreadyInRole {
        get {
            return ResourceManager.GetString("UserAlreadyInRole", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to UserId not found..
    /// 
    internal static string UserIdNotFound {
        get {
            return ResourceManager.GetString("UserIdNotFound", resourceCulture);
        }
    }
    
    /// 
    ///   Looks up a localized string similar to User {0} does not exist..
    /// 
    internal static string UserNameNotFound {
        get {
            return ResourceManager.GetString("UserNameNotFound", resourceCulture);
        }
    }
    

    0 讨论(0)
提交回复
热议问题