В порыве переработки своего сайта (некая область для приятелей с моими инструментами) дошёл я до переноса старого генератора паролей, написан он был ещё лет 5-7 назад и успешно выполнял свою функцию на протяжении этого времени. Но есть нюансы, он лишён каких-либо настроек, всего одна кнопка "сгенерировать" и поле в котором отображается пароль. Непорядок, подумалось мне, надо переделать. Родил вот такую вот штуку...
Сразу скажу, я не верстальщик и не дизайнер, но именно вот так вижу красивый интерфейс. Что у нас тут есть: ползуночки для регулирования соотношения разных категорий символов, поля с числовым отображением соотношения (в них можно и вписать сразу значение) в диапазоне от 0 (этой категории символов не будет) до 10, поле для заблокированных символов (ну вот не хочу в своём пароле я видеть например " * ") куда просто подряд вписываются все неприемлемые символы, выпадающий список с типами паролей (блочные: "qwer-tyui-1234-5678" и строковые: "qwertyui12345678") и настройки размеров. При выборе строчного пароля можно задать только его длину. А, да, ещё есть кнопка, самое важное же!
Быстренько сверстал карточки, и отправился создавать класс этого генератора. Использую я Blazor Server, значится писать мы будем на С#. Создаём наш PassGen и прописываем в него свойства и методы:
Давайте по порядку:
Result - это класс в котором у нас лежит password и error, оба свойства - это строки, сгенерированный пароль и сообщение при ошибке, соответственно.
Type - это у нас тип генерируемого пароля.
Characters - класс символов и всего к ним относящегося, остановимся на нём чуть подробней:
Тут мы храним numeric - число отражающее кол-во цифр в пароле, и аналогично с специальными символами, буквами нижнего регистра и верхнего. А ещё некий exclude - это строка с заблокированными символами. Дальше идут BlockBased и LineBased где по аналогии лежат настройки для размера блоков, количества блоков, разделителя и длина пароля в случае с линейным, ничего интересного. А внизу обитает Charset, он уже имеет парочку методов:
Тут у нас есть 4 листа для разных типов символов, в конструкторе они заполняются номерами по таблице ASCII. Я решил убрать все непечатные и тильту. А дальше метод Exclude в котором строка с забанеными символами убирается из каждого из этих массивов. Казалось бы, почему не собрать их все в один лист? Но нельзя пока, об этом дальше (вообще три из них можно, но упрощения это не даст). После идёт метод GetTotal, который собирает нам итоговый набор символов для генерации:
Создаём экземпляр класса Random, локальный буфер для итогового листа символов, а потом внезапно сортируем переданные переменные. Получаем самую маленькую из них и отправляем в цикл в качестве инициализирующего значения. Странный цикл, хрен пойми что вообще делает и за форматирование кода мне морду набьют. А этот цикл ищет наибольший общий делитель для всех четырёх переменных. Реализация странная, да и честно говоря это тут нахрен не нужно, но... Я могу себе это позволить. Дальше мы сокращаем все переменные соотношений и генерируем один большой массив согласно этим переменным. Если у нас соотношение 2:4:6:8, то мы добавим в итоговый набор одну пачку цифр, две пачки больших букв, три пачки маленьких и четыре пачки специальных символов. Самые внимательные заметили, что цифр мы добавляем в 3 раза больше, чем всего остального. и вот почему: в диапазоне от 33 до 125 у нас 27 малых букв, 27 больших и ~30 спецсимволов, а цифр всего 10, несправедливо это получается... А потом мы возвращаем этот лист не забыв предварительно его перемешать, да, тут и всплыл тот самый рандом, который создавался в самом начале. А создавался он там, чтоб растянуть по времени его инициализацию и инициализацию следующего рандома, так как это генератор ПСЕВДО случайных чисел и он опирается на состояние системы. (надо было использовать криптографический по-хорошему)
Замечательно, мы всё подготовили, теперь время ГЕНЕРИРОВАТЬ! А происходит это чуть ниже в методе Generate класса PassGen:
Тут мы создаём экземпляр Charset, чтоб он не висел в экземпляре постоянно. Просим очистить набор от мусора к которому добавляем и разделитель, чтоб он внутри блока не попался. Кормим ему соотношение символов, а получаем назад лист с номерами всех доступных для генерации символов в нужных пропорциях. Переписываем Result, чтоб новенький и чистенький был. Отсекаем хитрожопых "тестировщиков" пасхалочкой и переходим к самой генерации. Она крайне примитивна: для блоков берём их количество и запускаем цикл, в этом цикле проверяем, не первая ли это итерация, если первая - разделитель не суём. Генерируем размер текущего блока из промежутка min - max и запускаем цикл на это количество итераций. И в этом вложенном цикле просто к password приписываем случайно извлечённый из набора символ. Не забываем только его конвертировать в непосредственно символ, а то в наборе хранятся номера. С линейным всё ещё проще, там всего один цикл, но перед ним есть нюанс... Надо вернуть разделитель в коробку, а то мы его выше удалили. За сим всё, нужно присрать это к свёрстанной странице и назначить на кнопку этот самый Generate.
А вот скрин результата генерации с произвольными настройками:
Ну и по традиции: подписывайтесь на мой t.me/ ЗАЕБАЛИ СО СВОИМ ТЕЛЕГРАМОМ.
ПОЛЬЗОВАТЕЛЬ НЕ ВКЛЮЧАЛ ДОНАТЫ, ПЕРЕВЕСТИ ЕМУ ДЕНЬГИ НЕЛЬЗЯ, только пивом угостить лично.