どうもこんにちは。
ユーザのパスワードを設定するとき、複雑性を要求されることが多いですよね。
今回は、複雑性を満たしているかをチェックするために、
バリデーションルールをつくってみました。
作ったもの
早速ですが、コレを作りました。
2019/06/18 指摘があったので、一部修正しました。
MSさん、ありがとうございます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; class PasswordComplexity implements Rule { protected $minDigits; protected $maxDigits; protected $includeLessThanOneUpperLetter; protected $includeLessThanOneLowerLetter; protected $includeLessThanOneNumber; protected $includeLessThanOneSymbol; protected $includeSymbol; /** * Create a new rule instance. * * @param int $minDigits * @param null $maxDigits * @param bool $includeSymbol * @param bool $includeLessThanOneUpperLetter * @param bool $includeLessThanOneLowerLetter * @param bool $includeLessThanOneNumber * @param bool $includeLessThanOneSymbol */ public function __construct( $minDigits = 8, $maxDigits = null, $includeSymbol = true, $includeLessThanOneUpperLetter = true, $includeLessThanOneLowerLetter = true, $includeLessThanOneNumber = true, $includeLessThanOneSymbol = true ) { $this->minDigits = $minDigits; $this->maxDigits = $maxDigits; $this->includeLessThanOneUpperLetter = $includeLessThanOneUpperLetter; $this->includeLessThanOneLowerLetter = $includeLessThanOneLowerLetter; $this->includeLessThanOneNumber = $includeLessThanOneNumber; $this->includeLessThanOneSymbol = $includeLessThanOneSymbol; $this->includeSymbol = $includeSymbol; } /** * Determine if the validation rule passes. * * @param string $attribute * @param mixed $value * @return bool */ public function passes($attribute, $value) { // $regex = ''; if ($this->includeSymbol) { $regex = '[!-~]'; } else { $regex = '[a-zA-Z\d]'; } if ($this->includeLessThanOneLowerLetter) { $regex = "(?=.*?[a-z])" . $regex; } if ($this->includeLessThanOneUpperLetter) { $regex = "(?=.*?[A-Z])" . $regex; } if ($this->includeLessThanOneNumber) { $regex = "(?=.*?\d)" . $regex; } if ($this->includeLessThanOneNumber) { $regex = "(?=.*?[!-\/\:-@\[-`\{-~])" . $regex; } if ($this->maxDigits || $this->minDigits) { $regex = $regex . "{{$this->minDigits},{$this->maxDigits}}"; } $regex = "/\A{$regex}+\z/"; return preg_match($regex, $value); } /** * Get the validation error message. * * @return string */ public function message() { $message = 'パスワードは'; $includeLessThanOneLetters = []; if ($this->includeLessThanOneLowerLetter) { $includeLessThanOneLetters[] = "半角英字(小文字)"; } if ($this->includeLessThanOneUpperLetter) { $includeLessThanOneLetters[] = "半角英字(大文字)"; } if ($this->includeLessThanOneNumber) { $includeLessThanOneLetters[] = "半角数字"; } if ($this->includeLessThanOneSymbol) { $includeLessThanOneLetters[] = "半角記号"; } if ($includeLessThanOneLetters) { $message .= implode('、', $includeLessThanOneLetters) . "を1文字以上含む"; } if ($this->includeSymbol) { $message .= "半角英数字記号"; } else { $message .= "半角英数字"; } if ($this->minDigits) { $message .= "{$this->minDigits}文字以上"; } if ($this->maxDigits) { $message .= "{$this->maxDigits}文字以下"; } $message .= "で入力してください。"; return $message; } } |
簡単な解説
コンストラクタ
コンストラクタでは
- 最小文字数
- 最大文字数(null -> 無制限)
- パスワードに記号を含めるてよいか
- 1文字以上の半角英字(大文字)を含む必要があるか
- 1文字以上の半角英字(小文字)を含む必要があるか
- 1文字以上の半角数字を含む必要があるか
- 1文字以上の半角記号を含む必要があるか
を決めています。
public function passes($attribute, $value)
コンストラクタで与えられた値をもとに、
正規表現を組み立てています。
デフォルトのままであれば、最終的に
return preg_match("/\A(?=.*?[!-\/\:-@\[-`\{-~])(?=.*?\d)(?=.*?[A-Z])(?=.*?[a-z])[!-~]{8,}+\z/", $value);
となります。
あまり見慣れない”(?=xxxx)”があります。これは、「肯定先読み」と呼ばれるものです。
例えば
(?=.*?\d)
だと、.*?\d
の直前の文字にマッチします。
.
は改行以外の任意の1文字、
*?
は非貪欲的な正規表現(後に続く文字が1回でもマッチしたらそれ以降判定しない)、
\d
は0-9
と同義なので
つまり、
「改行以外の任意の1文字が0回以上繰り返されたのあとに半角数値が最低1文字続く」
↓
「半角数値が最低1文字ある」
という条件で文字列を先に探しておく、ということ(らしい)です。
これを、半角記号、半角英字(大文字)、半角英字(小文字)と連結させて、
「半角記号、半角英字(大文字)、半角英字(小文字)、半角数値を1文字以上含む」
を表現しています。
public function message()
ただ、メッセージをいい感じに作っているだけです。
さいごに
半分、正規表現の話でした。
r.tanakaがお届けしました。
$message .= “{$this->minDigits}文字以下”;
↓
$message .= “{$this->maxDigits}文字以下”;
正規表現で難儀してたので助かりました。
72行目
if ($this->includeLessThanOneNumber) {
は$this->includeLessThanOneSymbolですね。