🔑

パスワードを生成するライブラリを作った

2021/07/30に公開

https://github.com/Karibash/password-generator

使い方

import password from '@karibash/password-generator';

console.log(password(9));
// -> C_Tpmf45L

これだけで大文字、小文字、数字、記号を各1文字以上含む文字列を生成できます
探しても無かったので作っちゃいました

生成時に使用する文字を変更する

第2引数に文字配列を指定すると、生成時に使用する文字を変更出来ます

import password, { alphabetLowercase, alphabetUppercase } from '@karibash/password-generator';

const charSet = [
  alphabetLowercase,
  alphabetUppercase,
];
console.log(password(9, charSet));

これで大文字、小文字のみを含む文字列が生成できます

技術解説

主要な部分を抽出すると、こんな感じになってます
結構シンプルに実装出来てると思います

const choice = (chars: string): string => {
  return chars[Math.floor(Math.random() * chars.length)];
};

const shuffle = (chars: string): string => {
  return chars.split('').sort(() => 0.5 - Math.random()).join('');
};

const hash = (size: number, charSet: string[]): string => {
  if (size <= 0) return '';
  const chars = charSet
    .map(chars => {
      const count = Math.ceil(Math.random() * (size / charSet.length))
      return [...Array(count)].map(() => choice(chars)).join('');
    })
    .join('');
  return chars + hash(size - chars.length, charSet);
};

const password = (size: number, charSet: string[]): string => {
  if (size < charSet.length) console.warn('A value smaller than charSet.length is specified for the size argument.');
  return shuffle(hash(size, charSet)).substr(0, size);
};

choice関数

const choice = (chars: string): string => {
  return chars[Math.floor(Math.random() * chars.length)];
};

指定した文字列から1文字ランダムに取得する関数です
Math.random()で取得した乱数に文字列の長さを掛ける事で、ランダムに1文字取得出来るようになっています

shuffle関数

const shuffle = (chars: string): string => {
  return chars.split('').sort(() => 0.5 - Math.random()).join('');
};

文字列生成後の仕上げに文字列をシャッフルする関数です
文字列をsplit関数で一旦配列に変換し、sort関数でMath.random()を用いる事でランダムにソートしています
最後にjoin関数で繋げればシャッフルされた文字列の完成です

hash関数

const hash = (size: number, charSet: string[]): string => {
  if (size <= 0) return '';
  const chars = charSet
    .map(chars => {
      const count = Math.ceil(Math.random() * (size / charSet.length))
      return [...Array(count)].map(() => choice(chars)).join('');
    })
    .join('');
  return chars + hash(size - chars.length, charSet);
};

このライブラリの核となる関数です
charSetに対し逐次的に処理を行う事でランダムな文字列を生成しています

charSet毎の文字数を決める

const count = Math.ceil(Math.random() * (size / charSet.length))

まずcharSet毎の文字数をランダムに決定します
指定されたsizeをcharSetのlengthで割る事で最大文字数(n)を割り出しています
Math.random()に対して割り出した最大文字数(n)を掛ける事で、0~nの間で文字数をランダムに決定します
Math.ceil()で切り上げる事で最終的には1~nの間の文字数になります

割り出した文字数分の文字列をランダム生成する

return [...Array(count)].map(() => choice(chars)).join('');

[...Array(count)]で割り出した文字数分の配列を作成し、map関数でchoice関数を使用する事でランダムな文字を指定数分生成します
最後にjoinする事で配列から文字列に変換しています

再帰処理を行う

return chars + hash(size - chars.length, charSet);

最後に生成した文字列が指定した長さに満たない場合があるので、hash関数を再帰的に呼び出す事で足りない分を補っています

password関数

const password = (size: number, charSet: string[]): string => {
  if (size < charSet.length) console.warn('A value smaller than charSet.length is specified for the size argument.');
  return shuffle(hash(size, charSet)).substr(0, size);
};

このライブラリのメイン関数です

if (size < charSet.length) console.warn('A value smaller than charSet.length is specified for the size argument.');

sizeに対してcharSet配列の長さより短い値が指定された場合に警告文を表示するようにしています
Errorをthrowするかどうか悩んだのですが、警告文表示にしておきました

return shuffle(hash(size, charSet)).substr(0, size);

hash関数で生成した文字列をshuffle関数でシャッフルしています
hash関数で再帰処理を行っている影響で文字列の長さがsizeより大きくなる場合があるので、シャッフル後にsubstr関数でsize分の長さに補正しています

Discussion