はじめに
こんにちは。Laravelには標準の認証機能Authが用意されており、その中にはパスワードリセット機能が最初から含まれています。 メールアドレスを入力するとメールが送信され、メール本文内のURLにアクセスしてパスワードを再設定できるという、様々なサイトにある機能です。
今回はこのパスワードリセット機能を一部カスタマイズすることで、Laravelの認証機能のカスタマイズ方法を追っていきたいと思います。
環境はmacOS High Sierra+Laradockで構築し、LaravelのバージョンはLTS版の最新である5.5を使用します。また、Docker本体のインストール完了、およびLaradockで環境構築しWelcomeページが表示できていることが前提となります。
準備
Auth
ログイン画面等のviewは、Laravel標準で提供されている
auth
を利用します。PHPが動作しているコンテナにログインし、
artisan
コマンドを実行することで自動生成できます。
1 2 3 4 | # コンテナにログイン $ docker-compose exec --user=laradock workspace bash # authインストール $ php artisan make:auth Authentication scaffolding generated successfully. |
mailhog
mailhogは簡易SMTPサーバの一種で、mailhogに対してPHPやRubyなどからメール送信処理を実行することで、実際のアドレスに送信することなく、メールの内容を確認することができます。 mailhogのコンテナはLaradockに含まれており、build&upで簡単に立ち上げることができます。
1 2 3 4 5 6 7 8 | $ docker-compose build --no-cache mailhog<br>Building mailhog Step 1/4 : FROM mailhog/mailhog ... ... Successfully built f1f39fbd927d Successfully tagged laradock_mailhog:latest $ docker-compose up -d mailhog Recreating laradock_mailhog_1 ... done |
立ち上げた後は
localhost:8025
にアクセスすると、mailhogに送信したメールをブラウザから確認できます。なお、mailhogの設定はdocker-compose.ymlに記述されており、デフォルトでは下記のようになっています。 この場合、1025番ポートでメールを待ち受けており、8025番ポートでブラウザからのアクセスを待ち受けているという意味になります。
1 2 3 4 5 6 7 8 | mailhog: build: ./mailhog ports: - "1025:1025" - "8025:8025" networks: - frontend - backend |
続いてLaravelのメール送信設定を変更します。Laravelプロジェクトのトップディレクトリにある
.env
を編集し、デフォルトから以下のように変更します。
1 2 3 4 5 6 | MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io → mailhog MAIL_PORT=2525 → 1025 MAIL_USERNAME=null → user MAIL_PASSWORD=null → password MAIL_ENCRYPTION=null |
これでLaravelから送信されるメールをmailhogでキャッチし、実際にメールアドレスに送信することなくメール内容を確認できるようになりました。
ユーザー登録とログイン
localhost/register
にアクセスすると登録フォームが表示されます。 任意のアカウント名・メールアドレス・パスワードを入れて登録を行い、ログイン完了した旨が表示されたら、画面右上のリンクからログアウトしましょう。
パスワードリセットのカスタマイズ
http://localhost/login
で「Forgot Your Password?」をクリックすると、メールアドレス入力画面が表示されます。 デフォルトではここで入力したメールアドレスにパスワードリセット用のURLが送信されますが、今回はアカウント名でリセットを行うようにカスタマイズしてみます。
ルーティング確認
「どのURLにアクセスしたらどのアクションメソッドが呼ばれるか」を確認するにはいくつか方法がありますが、ここでは
artisan
コマンドを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ php artisan route:list +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/user | | Closure | api,auth:api | | | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web,auth | | | GET|HEAD | login | login | App\Http\Controllers\Auth\LoginController@showLoginForm | web,guest | | | POST | login | | App\Http\Controllers\Auth\LoginController@login | web,guest | | | POST | logout | logout | App\Http\Controllers\Auth\LoginController@logout | web | | | POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail | web,guest | | | GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web,guest | | | POST | password/reset | | App\Http\Controllers\Auth\ResetPasswordController@reset | web,guest | | | GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm | web,guest | | | GET|HEAD | register | register | App\Http\Controllers\Auth\RegisterController@showRegistrationForm | web,guest | | | POST | register | | App\Http\Controllers\Auth\RegisterController@register | web,guest | +--------+----------+------------------------+------------------+------------------------------------------------------------------------+--------------+ |
メールアドレス入力画面のURLはpassword/resetですので、対応しているのは下記の行となります。
GET|HEAD | password/reset | password.request | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm
ところが
ForgotPasswordController
を開いてみると
showLinkRequestForm
は存在せず、実際の処理は
SendsPasswordResetEmails
のshowLinkRequestFormで行われています。 これで
auth/passwords/email.blade.php
を呼び出しているのが分かりました。viewのファイル名を変えたり、viewを呼び出す以外の処理を行いたい場合は
ForgotPasswordController
クラスで
showLinkRequestForm
をオーバーライドすることでカスタマイズが可能です。
1 2 3 4 5 6 7 | trait SendsPasswordResetEmails { public function showLinkRequestForm() { return view('auth.passwords.email'); } ... ... |
view編集
auth/passwords/email.blade.php
を以下のように編集し、メールアドレスではなくアカウント名を入れるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}"> <label for="name" class="col-md-4 control-label">Input your name.</label> <div class="col-md-6"> <input id="name" type="text" class="form-control" name="name" value="{{ old('name') }}" required=""> @if ($errors->has('name')) <span class="help-block"> <strong>{{ $errors->first('name') }}</strong> </span> @endif </div> </div> |
認証処理の編集
viewの編集が完了したら、ユーザー登録したときのアカウント名を入力して「Send Password Reset Link」を…と言いたいところですが、本来はメールアドレスの入力を想定した機能ですので、アカウント名を入力しても動作せず、エラー扱いになってしまいます。認証部分の処理もカスタマイズしましょう。
auth/passwords/email.blade.php
のform要素を見ると、送信先は
password/email
が指定されています。
1 | <form class="form-horizontal" method="POST" action="{{ route('password.email') }}"> |
先述の
php artisan route:list
で該当するのは以下の行です。
POST | password/email | password.email | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail
ここでも
ForgotPasswordController
に該当のメソッドは存在せず、実体は
SendsPasswordResetEmails
に実装されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public function sendResetLinkEmail(Request $request) { $this->validateEmail($request); $response = $this->broker()->sendResetLink( $request->only('email') ); return $response == Password::RESET_LINK_SENT ? $this->sendResetLinkResponse($response) : $this->sendResetLinkFailedResponse($request, $response); } protected function validateEmail(Request $request) { $this->validate($request, ['email' => 'required|email']); } |
今回はこれらのメソッドを
ForgotPasswordController
クラスの中でオーバーライドして、下記のように実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class ForgotPasswordController extends Controller { ... ... public function sendResetLinkEmail(Request $request) { $this->validateUserName($request); $response = $this->broker()->sendResetLink( $request->only('name') ); return $response == Password::RESET_LINK_SENT ? $this->sendResetLinkResponse($response) : $this->sendResetLinkFailedResponse($request, $response); } protected function validateUserName(Request $request) { $this->validate($request, ['name' => 'required']); } } |
ここまで完了したら
http://localhost/password/reset
でアカウント名を入力し「Send Password Reset Link」ボタンを押しましょう。 成功すれば「We have e-mailed your password reset link!」と表示されて、登録時に入力したメールアドレスにメールが送信…は実際には行われません。
localhost:8025
にアクセスすると、webメールサービスの受信画面のようなページが表示され、ここで送信内容が確認できます。
以下2つは前述の
mailhog
の一覧・詳細画面です。通常のSMTPサーバであればLaravelからメールアドレス宛に送信が行われますが、
mailhog
がメールをキャッチし、外部への送信を行わずSMTPサーバ内に留めて、このように表示しています。
リセット画面
メールに表示されている「Reset Password」のリンクにアクセスすると、新しいパスワードを入力する画面が表示されます。ここでのルーティング情報は下記のとおりです。
GET|HEAD | password/reset/{token} | password.reset | App\Http\Controllers\Auth\ResetPasswordController@showResetForm
今までと同じく
ResetPasswordController
に
showResetForm
メソッドは無く、実装は
ResetsPasswords
で行われています。
1 2 3 4 5 6 7 8 9 10 | trait ResetsPasswords { ... ... public function showResetForm(Request $request, $token = null) { return view('auth.passwords.reset')->with( ['token' => $token, 'email' => $request->email] ); } |
このメソッドをカスタマイズする際も、先ほどの
sendResetLinkEmail
と同パターンで、ここでは
ResetPasswordController
でオーバーライドすることになります。 実際のリセット処理や、リセット完了後のページ転送処理も
ResetsPasswords
に実装されており、それぞれカスタマイズが可能です。
さいごに
いかがでしたでしょうか。1機能の条件をひとつ変えるだけで随分面倒な…と感じるかもしれませんが、実装方法を細かく指定することはフレームワーク(枠組み)の意図するところであり、こうした学習コストと引き換えに、メソッドの命名や処理の遷移が実装者に左右されなくなるというメリットを得られるのだと思います。