Валидация пароля

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 15.12.2019

Система ASP.NET Identity уже имеет ряд ограничений на вводимые данные, которые нам надо использовать. Например, если мы возьмем любой проект с ASP.NET Core Identity, например, созданный в прошлых темах, то мы заметим, что для валидации пароля в Identity уже заложен ряд ограничений:

Валидация пароля в ASP.NET Core

При вводе пароля срабатывает встроенная логика, согласно которой длина пароля не может быть меньше 6 символов, пароль должен содержать как минимум один цифровой и один не алфавитно-цифровой символ, а также как минимум один алфавитный символ должен быть в верхнем регистре. Но что, если нам надо установить другую минимальную длину пароля? Или что если мы хотим, чтобы в пароле могли бы использоваться только цифровые или только алфавитные символы?

И несмотря на то, что встроенная логика валидации скрыта от наших глаз, мы ее можем переопределить с помощью объекта Microsoft.AspNetCore.Identity.PasswordOptions.

Чтобы его применить, необходимо изменить код добавления контекста в классе Startup в методе ConfigureServices(), который по умолчанию имеет следующее содержимое:

services.AddIdentity<User, IdentityRole>()
		.AddEntityFrameworkStores<ApplicationContext>();

Итак, изменим данный код следующим образом:

services.AddIdentity<User, IdentityRole>(opts=> {
        opts.Password.RequiredLength = 5;   // минимальная длина
        opts.Password.RequireNonAlphanumeric = false;   // требуются ли не алфавитно-цифровые символы
        opts.Password.RequireLowercase = false; // требуются ли символы в нижнем регистре
        opts.Password.RequireUppercase = false; // требуются ли символы в верхнем регистре
        opts.Password.RequireDigit = false; // требуются ли цифры
    })
    .AddEntityFrameworkStores<ApplicationDbContext>();

Класс PasswordOptions предоставляет следующие свойства:

  • RequiredLength: минимальная длина пароля

  • RequireNonLetterOrDigit: если равно true, то пароль должен будет иметь как минимум один символ, который не является алфавитно-цифровым

  • RequireDigit: если равно true, то пароль должен будет иметь как минимум одну цифру

  • RequireLowercase: если равно true, то пароль должен будет иметь как минимум один символ в нижнем регистре

  • RequireUppercase: если равно true, то пароль должен будет иметь как минимум один символ в верхнем регистре

Если мы меняем длину пароля, то соответственно нам надо изменить определение модели регистрации, если она устанавливает ограничение на длину:

public class RegisterViewModel
{
	[Required]
	[Display(Name = "Email")]
	public string Email { get; set; }

	[Required]
	[Display(Name = "Год рождения")]
	public int Year { get; set; }

	[Required]
	[DataType(DataType.Password)]
	[StringLength(100, ErrorMessage = "Поле {0} должно иметь минимум {2} и максимум {1} символов.", MinimumLength = 5)]
	[Display(Name = "Пароль")]
	public string Password { get; set; }

	[Required]
	[Compare("Password", ErrorMessage = "Пароли не совпадают")]
	[DataType(DataType.Password)]
	[Display(Name = "Подтвердить пароль")]
	public string PasswordConfirm { get; set; }
}

Для большинства случаев настроек класса PasswordOptions вполне хватит, мы можем настроить минимальную длину пароля, ввод алфавитных, цифровых или не алфавитно-цифровых символов. Однако если нам потребуется более сложная логика валидации, то придется определять свой класс валидатора.

Объект валидатора представляет объект интерфейса IPasswordValidator<T>:

public interface IPasswordValidator<T> where T : class {

	Task<IdentityResult> ValidateAsync(UserManager<T> manager, T user, string password);
}

Метод ValidateAsync() вызывается при валидации пароля. В качестве параметров он получает объект UserManager, объект пользователя и сам пароль, который надо валидировать.

Результат метода представляет объект IdentityResult, через статус которого можно узнать об успешности валидации: если свойство Succeeded равно true, то валидация прошла успешно.

Итак, создадим новый класс, который назовем и который будет иметь следующий код:

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;

namespace CustomIdentityApp.Models
{
    public class CustomPasswordValidator : IPasswordValidator<User>
    {
        public int RequiredLength { get; set; } // минимальная длина

        public CustomPasswordValidator(int length)
        {
            RequiredLength = length;
        }

        public Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user, string password)
        {
            List<IdentityError> errors = new List<IdentityError>();

            if (String.IsNullOrEmpty(password) || password.Length < RequiredLength)
            {
                errors.Add(new IdentityError
                {
                    Description = $"Минимальная длина пароля равна {RequiredLength}"
                });
            }
            string pattern = "^[0-9]+$";

            if (!Regex.IsMatch(password, pattern))
            {
                errors.Add(new IdentityError
                {
                    Description = "Пароль должен состоять только из цифр"
                });
            }
            return Task.FromResult(errors.Count == 0 ?
                IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
        }
    }
}

Для проверки на длину в классе определено свойство RequiredLength. Кроме проверки количества символов пароль здесь также проверяется на соответствие регулярному выражению, которое говорит о том, что все символы должны представлять цифры (string pattern = "^[0-9]+$"). Таким образом, мы можем определить в методе различные способы проверок.

Если какая-то из проверок не будет пройдена, то в список ошибок добавляется очередная ошибка в виде объекта IdentityError. При отстутствии ошибок возвращается результат IdentityResult.Success.

Для использования класса перейдем в класс Startup и изменим его метод ConfigureServices():

public void ConfigureServices(IServiceCollection services)
{
	// Добавляем сервис валидатора пароля
	services.AddTransient<IPasswordValidator<User>,
			CustomPasswordValidator>(serv => new CustomPasswordValidator(6));

	services.AddDbContext<ApplicationContext>(options =>
		options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

	services.AddIdentity<User, IdentityRole>()
			.AddEntityFrameworkStores<ApplicationContext>();
            
	services.AddControllersWithViews();
}

Первый вызов добавляет зависимость для интерфейса IPasswordValidator в виде объекта CustomPasswordValidator.

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

Создание валидатора пароля в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850