Laravel5.5LTS、AtoZ#5、組み込み認証機能

タグ: Laravel5.5  

インストール直後のデフォルト状態では、password_resetsという認証の一部で使用するテーブルを作成するマイグレーションは含まれていますが、認証機能自体は組み込まれていません。Laravelが用意している認証を利用するか、それとも別の認証Composerパッケージを利用するか、はたまた自前で構築するかは皆さん次第です。

今回はLaravelを一通り使ってみるシリーズですので、もちろん組み込み済みの認証システムを利用します。組み込み済み認証機能がどの程度皆さんの要求を満たしているかを確認しましょう。

テーブル

認証で作成されるユーザー情報は、データベースへ保存されます。実のところ、前回のシリーズ4の記事で実行したマイグレーションにより、作成されたusersテーブルがそれです。

もちろん、認証以外の情報をこのテーブルに直接入れることも可能ですし、リレーションを利用して別テーブルに用意することも可能です。(込み入りますので、別の機会にしましょう。)

ユーザーはパスワードをよく忘れます。重要なサイトやよく使うサイトであれば、ユーザー自身が覚えているか、ツールで管理してるか、ブラウザが記録しています。しかし、たいして使わないし、重要でないと思っており、新しいブラウザを使い始めて古いやつは削除したなんて状況ででは、確実に思い出せません。

そのため、登録したメールアドレスを思い出してもらえば、そのメールアドレスへ新しいパスワードを登録するためのリンクを入れてメールを送信します。(メールへパスワードを含めるのはセキュリティリスクです。)ユーザーはそのリンク先で、新しいパスワードを登録すると、再びログインできるようになります。

Laravelでは、新しいパスワードを登録するURLへ一時的なトークンを付けています。これを管理しているのが、password_resetsテーブルです。このトークンがリセットメール発行時に生成したものと同じでなければ、パスワードリセットはできません。このトークンはできるだけ短い有効期限にします。長いほどセキュリティリスクが高まります。つまり、第三者によりリセットされ、アカウントが乗っ取られる可能性が増えます。(メールの盗聴を心配しているわけです。ユーザーがリセットを希望した、すぐにメールが届く、中のリンクをクリックする、表示されたページで新しいパスワードを登録するという一般的なシナリオをLaravelはサポートしています。メールが届いてから中のリンクをクリックしてもらうまで時間が長いと、メールが盗聴されていた場合に第三者がメールの内容にアクセスできる可能性が上がります。メールは内容を盗みづらい通信手段ですが、絶対はありません。ユーザー側のコンピュータにウイルスを仕込めば、簡単に盗めるでしょう。メールを送受信、中継する会社や担当者が悪意を持っている可能性もあります。)

Userクラス

Laravelはアクティブレコードをサポートしています。Eloquentと言う名前です。テーブルのレコードをモデルインスタンスとして扱う手法です。

Laravelの開発者であるTaylorさんは、LaravelはMVCだと言っていません。バージョン3の最初の頃までは言っていましたが、立場を変えました。MVCがどのようなものであるか、開発者の意見はばらばらで、特にモデルに関して不統一や誤解がはびこりました。

Laravelでモデルと言うときは、MVCのモデルではなく、Eloquentのモデルと言う意味で使われることが多いです。

モデルに対する個人の見解の違いを尊重するため、Laravelのデフォルトでは、本来「アプリケーション」を表すappディレクトリー直下にUser.phpファイルが置かれています。これは、「Laravelではapp直下に置くのがしきたりだ」という意味でありません。「モデルに対する皆さんの意見に従い、適切なディレクトリー(PHPクラスでは名前空間)を付けて管理してください。」という意味合いです。(私の意見ではありません。Taylorさん自身が何度もいろいろな場面でこのように説明しています。)

Eloquentを開発に使用するかも開発者である皆さんの自由ですが、使いながら開発を進めるとapp下にモデルのファイルが増え、かっこよくありません。適切なディレクトリー下で管理しましょう。

今回はEloquentModelsというディレクトリーを作成し、その下で管理します。app下に作成してください。

続いてUser.phpファイルを作成したEloquentModelsディレクトリーへ移動します。

ディレクトリーを変更しました。クラスのオートロードのPSR-4規約に従い、PHPの名前空間もそれに合わせる必要があります。User.phpファイルを開き、先頭にある名前空間を合わせましょう。

namespace App\EloquentModels;

Userモデルの変更はこれ以上必要ありません。

次に認証の設定を変更します。認証は「認証済みのユーザー情報をどう扱うか」を設定により判断しています。「デフォルトではEloquentのApp\Userクラス」です。UserクラスをEloquentModelsディレクトリー下に移動する前は、デフォルトのApp/Userクラスでした。これをApp/EloquentModels/Userに変えたわけです。変更後に設定を合わせる必要があります。

config/auth.php設定ファイルを開き、変更します。

...
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\EloquentModels\User::class
        ],
...

これで、認証システムは認証済みのユーザー情報をUserモデルのインスタンスとして提供してくれます。

スカフォールド

もちろん、認証を行うためのコードはLaravelにもちろん初めから含まれています。ただし、動作させるにはビューとルート定義、言語ファイルなどが必要です。こうしたリソースは、コマンドで一気に用意できます。

php artisan make:auth

welcome.blade.phpビューファイルを次の内容に変更してください。

@extends('layouts.app')

@section('content')
    <div class="content">
        <div class="jumbotron">
            <div class="container">
                <h1>ドーン</h1>
                <p>通常のテキスト</p>
                <p><a href="#">リンク色</a></p>
            </div>
        </div>
    </div>
@endsection

これで、ユーザー登録のregisterボタンと、loginボタンが表示されるようになりました。

実際にユーザー登録を試してみると、エラーになります。スカフォールドで生成される、app/Http/Controllers/Auth/RegisterController.phpのUse文で、App\Userがハードコートされているからです。

本来、ここはauth.php設定ファイルの内容を適用するのが妥当です。しかし、このコントローラーコードはユーザーが自由に変更できるものです。(appディレクトリー内に存在=ユーザーが変更可能なPHPコード)直接指定しているのは、コードが複雑になることを避けたのだと推測します。推測があたっていようと、外れていようと、いずれにせよuse文を修正しましょう。

use App\EloquentModels\User;

まずは、ユーザー登録と、ログイン/ログアウトを試して動作を観察してください。

Mixを使っている場合、httpsでの通信がWebサーバーによりブロックされます。Mixを使用するならばhttp通信にするか、https通信にこだわるならMixを使用せず(ポート番号を付けない)に試してください。

認証済みユーザー専用ページ

ログイン後は、/homeのURLへリダイレクトされます。これは「ログインユーザー専用のダッシュボード」を表しています。もちろん、開発時は自由に変更可能です。

一つ、ルートを増やしましょう。/senyou URLへアクセスしたら、「専用ページ」とだけ大きく表示します。このページは、認証中のユーザーのみアクセス可能にします。そのため、authミドルウェアを指定します。

routes/web.phpファイルの最後に、次のルートを追加してください。

Route::get('/senyou', function() {return '<h1>専用ページ</h1>';})
    ->middleware('auth');

これでルートは、次のようになります。

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
GET HEAD senyou Closure | web,auth

この内容は、php artisan route:listで表示できます。(テーブルの表示右側が欠けますが、そのためにこのサイトのCSSは今回修正しません。コンソール表示を自分の目で確認してください。)

ざっと見てみましょう。/はルートのURLを意味します。ルートページの表示です。login URLはログイン、logout URLはログアウト、register URLはユーザー登録、password/emailはパスワードリセットのためのメール送信までを行う処理、password/resetはリセットメール中に埋め込んでいるURLで実際のパスワードをリセットする処理です。senyouは最後に追加したルートです。

これを踏まえて、再度いろいろと動作確認してください。ログイン状態、未ログイン状態で/senyouページヘアクセスしてください。Laravelのデフォルト認証動作は、皆さんの要求と合っているでしょうか。