laravel5.5でパスワードリセットを自前で実装してみる
laravelにはパスワードリセットが標準で実装されているが、emailでユーザ識別している。通常はemailはユニークキーなので問題ないが
usersテーブルでemailのユニーク制限を削除していると、うまく動作しない(sectionやcompanyなど複合キーでunique化している場合など)
※password_resetsテーブルの実装が、email・urlトークン・created_atしか保持していない
なので、もっと簡単な感じになるように変更してみた。
仕様その1,パスワードリセット画面では、メアド&秘密の質問で認証
仕様その2,パスワードリセット・メール送信した時点で、パスワードは上書き(乱数をセット)
仕様その3,パスワードリセット・メールには、IDと乱数の仮パスワードが記載されている
1, routes/web.phpにルーティング
LoginControllerに実装しているけど、独自コントローラーや別のでもいい。
1 2 3 4 |
// パスワードリセットのためのemail入力 Route::get('password/reset', 'Auth\LoginController@showPasswordResetForm')->name('password.request'); // リセットしたパスワードとログインIDが書かれたメール送信 Route::post('password/email', 'Auth\LoginController@sendPasswordResetEmail')->name('password.email'); |
2, パスワードリセット・メール送信クラスを生成
app/Mail/EmailPasswordReset.phpが生成される。
1 |
php artisan make:mail EmailPasswordReset |
メールのタイトルやテンプレート・変数を設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// クラス生成時にユーザ情報と新規パスワードを確保 protected $user; public function __construct($user, $new_password) { $this->user = $user; $this->new_password = $new_password; } // メール送信処理 public function build() { // メールのタイトル・bladeファイル・変数をセット return $this ->subject('パスワードがリセットされました') ->view('auth.email.password_reset') ->with([ 'username' => $this->user->username, 'new_password' => $this->new_password ]); } |
3, メールの内容を記述。以下のファイルを自分で作る
resources/views/auth/email/password_reset.blade.php
1 2 3 4 5 |
パスワードがリセットされました。<br> <br> 以下のアカウント情報でログインしてください。<br> ログインID: {{ $username }}<br> ログイン・パスワード: {{ $new_password }} |
4, メール送信後のサンクス画面を作る
resources/views/auth/passwords/thanks.blade.php
1 2 |
パスワードがリセットされました。 ログインID・ログインパスワードが記載されたメールを送信しましたので、ご確認下さい。 |
5, パスワードリセットのための認証・パスワード上書き処理・メール送信を記述する
app/Http/Controller/Auth/LoginController.php
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 |
use App\User; use Mail; // メール送信 use App\Mail\EmailPasswordReset; // パスワードリセット・メール送信 // パスワードリセット申し込み画面。メアドと秘密の質問の答えを入力する public function showPasswordResetForm() { // ID・パスワードが確認済み(SectionAuthMiddleware) $section = Section::where('section_auth_id', session('section_auth_id'))->First(); return view('auth.passwords.email', compact('section')); } // 認証に問題がなければ、乱数でパスワード上書き。ログインIDと一緒にメール送信する public function sendPasswordResetEmail(Request $request) { $data = $request->all(); // form入力値 $user = User::where('section_id', $section->id) ->where('email', $data['email']) ->where('secret_question', $data['secret_question']) ->where('secret_answer', $data['secret_answer']) ->first(); // 本人確認が取れなかったら if (!$user) { \Session::flash('error_status', '登録した内容では、パスワードの再発行は出来ません'); return back()->withInput(); // 本人確認が取れたら } else { // 乱数パスワードで上書き $new_password = self::generate_password(); $user->password = bcrypt($new_password); $user->save(); // ログインIDとパスワードが入ったメールを送信 $email = new EmailPasswordReset($user, $new_password); Mail::to($user->email)->send($email); return view('auth.passwords.thanks'); } } // 大文字1文字以上・小文字1文字以上・数字1文字以上の8文字固定の乱数生成 public function generate_password($password_length = 8) { $char_seed = [ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', '0123456789' ]; $password_string = ""; //シードの種類だけループ for($i=0; $i<count($char_seed); $i++){ // 各シードから1文字だけ抽出するindexを生成 $index = mt_rand(0, mb_strlen($char_seed[$i]) - 1); // 各シードから1文字だけ抽出 $password_string .= mb_substr($char_seed[$i], $index, 1); } // 残りの文字は乱数 $password_string .= str_random($password_length - count($char_seed)); // 順不同に、かき混ぜる return str_shuffle($password_string); } |
6, メールに書いてある乱数パスワードで、ログインできれば完了!