owned mediaウェブ制作に役立つコンテンツを発信中!

Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#5:ログイン認証機能の実装

最終更新日: Update!!
前回記事「Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#4:ログイン認証ユーザー作成」ではログイン認証のテストユーザーをシーダーファイルで作成するところまで進めてきましたが、今回は実際にログイン機能を実装していきます。今回はとりあえず基本的なものとして、メールアドレスとパスワードを使った認証を作成してみたいと思います。   この記事では、ログイン用のテストユーザーが作成されており、基本的なアプリケーションの環境構築が出来上がっている前提で進めていきます。これまでの内容については過去記事等をご参考ください。 (これまでの内容はこちらの記事から) Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#1:Docker環境構築 Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#2:Laravelのインストール Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#3:静的ページの作成とルーティング Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#4:ログイン認証ユーザー作成    
1. ログインページのViewファイル作成
まずはログイン用画面のViewファイルを用意しておきます。下記のように「resources/views」ディレクトリ内にログイン画面用のディレクトリを作成して、Bladeファイルを配置します。
$ mkdir resources/views/signin
$ touch resources/views/signin/index.blade.php
  作成後はこのようなファイル構成となりました、ここからControllerなどの処理を追加していきます。
resources
  ┣ views
    ┣ signin
      ┗ index.blade.php
   
2. ログインページ用のControllerを作成
続いてログインページ用のControllerファイルを作成します。こちらも過去記事(Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#3:静的ページの作成とルーティング)で紹介した通り、コマンドで簡単に作成することができますので、それを利用します。
$ docker-compose exec app php laravel/artisan make:controller SigninController
  ログインページ用のControllerファイルが作成できたら、先にログインページ用のViewファイルを出力させる処理を用意します。またこのControllerファイルでは、ログイン認証の処理も追加していきますので、Authファサードを利用する形となります。ですので先に読み込んでおきます。 【app/Http/Controllers/SigninController.php】
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class SigninController extends Controller
{
  public function index() {
    return view('signin.index');
  }
}
   
3. ログインページのルーティング設定
次は先ほど作成した、Viewファイルを表示させるためのURLをルーティングの設定で有効にしていきます。GETメソッドで、Viewファイルが呼び出される処理を実行し、ページが表示されるように設定しておきます。 【routes/web.php】※一部抜粋
<?php

use Illuminate\Support\Facades\Route;

.......
// signin
Route::get('/signin', 'SigninController@index')->name('signin');
  これでひとまずはログインページが表示されるようになりました。ただ、まだログインフォームや認証機能はありませんので、これから作成していきます。    
4. Controllerでログイン認証処理を追加
ログインページ用のControllerファイルにログイン認証機能の処理を加えていきます。こちらは最終的にPOSTメソッドで呼び出されるようになります。フォームの入力値を「validate()」メソッドの引数に入れることでバリデーションにかけることができます。そして、値を「Authファサード」にある「attempt()」メソッドでログイン情報の認証を行います。このメソッドの返り値で認証が成功した時の処理と失敗した時の処理を分岐させることができます。 【app/Http/Controllers/SigninController.php】※一部抜粋
class SigninController extends Controller
{
  ......
  public function signin(Request $request) {
    $authentication = $request->validate([
      'email' => ['required', 'email'],
      'password' => ['required'],
    ]);
    if(Auth::attempt($authentication)) {
      $request->session()->regenerate();
      return redirect()->intended(route('home'));
    } else {
      return back()->withErrors([
        'result' => 'ログインに失敗しました'
      ]);
    }
  }
}
  上記ではログイン認証が成功するとセッションが生成されアプリケーションのトップページにリダイレクト、失敗するとエラーメッセージの受け渡しとログインページにリダイレクトされる処理を「back()」メソッドを使って実装します。    
5. 認証処理用のルーティングを追加
認証処理が用意できれば、これを実行するためのルーティングを定義します。先ほどGETメソッドでページを表示させる処理を呼び出していましたが、POSTメソッドでは先ほど作成した認証処理が呼び出されるようにします。 【routes/web.php】※一部抜粋
<?php

use Illuminate\Support\Facades\Route;

.......
// signin
Route::get('/signin', 'SigninController@index')->name('signin');
Route::post('/signin', 'SigninController@signin');
  これで裏側の処理は出来上がってきましたので、続いてフロント側のフォームUIも作成していきます。    
6. ログインフォーム設置
ログインページ内のViewファイルにフォーム要素をHTMLで作成します。この際に「@csrf」を加えることで、CSRFトークンを生成するinput要素を設置することができます。各入力フォームのname属性は、認証機能の処理に入る値のキーと合わせておき、POSTメソッドで、ログイン画面のルーティングで設定したURLに対して送信するようにします。 【resources/views/signin/index.blade.php】※一部抜粋
<form method="post" action="{{ route('signin') }}">
  @csrf
  <div>
    <label>メールアドレス</label>
    <input type="email" name="email" placeholder="例)info@example.com">
  </div>
  <div>
    <label>パスワード</label>
    <input type="password" name="password">
  </div>
  <input type="submit" value="サインイン">
</form>
  これでログイン認証はできるようになりますが、ログアウト処理がまだ用意されていませんので、これから作成していきます。    
7. ログアウト処理を追加
ログイン処理と同じようにログイン画面用のControllerにログアウトの処理を追加していきます。ログアウトの方はAuthファサードの「logout()」メソッドを使うことで簡単に実装できます。そして今回はログアウト後にはアプリケーションのトップページにリダイレクトされるようにしています。 【app/Http/Controllers/SigninController.php】※一部抜粋
class SigninController extends Controller
{
  ......
  public function signout(Request $request) {
    Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect(route('home'));
  }
}
  ログアウト処理ができれば、ログアウト用のルーティングを作成します。これで、ログアウト用のURLにアクセスすることでログアウトされるようになります。 【routes/web.php】※一部抜粋
<?php

use Illuminate\Support\Facades\Route;

.......
// signin & signout
Route::get('/signin', 'SigninController@index')->name('signin');
Route::post('/signin', 'SigninController@signin');
Route::get('/signout', 'SigninController@signout');
  ここまで問題なく進めていれば、ログイン認証処理ができている形になります。ただ、現状ではログイン認証済みかどうかが画面上ではわからないため、ログイン認証済みかどうかで分岐処理を加えたり、リダイレクト処理を追加していきます。    
8. ログイン状態で分岐表示
ログイン認証済みの場合にはログアウト処理のリンクを、未ログインの場合にはログイン画面へのリンクが表示されるように分岐を加えていきます。Bladeでは認証済みかどうかで分岐できるディレクティブが用意されているので、そちらを利用します。(参考記事:LaravelのViewテンプレートBladeで使う基本のディレクティブまとめ) 【resources/views/home/index.blade.php】※一部抜粋
.....
@section('content')
  <main>
    @auth
      <a href="{{ route('signout') }}">サインアウト</a>
    @endauth
    @guest
      <a href="{{ route('signin') }}">サインイン</a>
    @endguest
  </main>
@endsection
.....
   
9. ログイン中にログインページのリダイレクト設定を追加
続いて、ログイン中にはログイン画面にアクセスできないようにするため、リダイレクト処理を加えていきます。先ほど作成したログイン画面のViewファイルを呼び出す処理にログイン状態かどうかの条件分岐を加えます。ログイン状態をチェックする際には、Authファサードの「check()」メソッドを使うと真偽値を返す形でチェックすることができます。下記ではログイン中の時にログイン画面にアクセスするとアプリケーションのトップへリダイレクトされるように設定しています。 【app/Http/Controllers/SigninController.php】※一部抜粋
class SigninController extends Controller
{
  ......
  public function index() {
    if (Auth::check()) {
      return redirect()->intended(route('home'));
    } else {
      return view('signin.index');
    }
  }
}
   
10. バリデーションエラーメッセージの表示
ログイン画面で認証が失敗した場合にはリダイレクトされるように設定していましたが、合わせてエラーメッセージを受け渡すことで、リダイレクト後の画面でエラーメッセージを表示させることができます。「validate()」メソッドの第一引数にはバリデーションルールの設定が入りますが、第二引数にはバリデーションルール対応するメッセージを指定します。 【app/Http/Controllers/SigninController.php】※一部抜粋
class SigninController extends Controller
{
  ......
  public function signin(Request $request) {
    $authentication = $request->validate(
      [
        'email' => ['required', 'email'],
        'password' => ['required'],
      ],
      [
        'email.required' => 'メールアドレスを入力してください',
        'email.email' => 'メールアドレスの形式が異なります',
        'password.required' => 'パスワードを入力してください',
      ]
    );
    if(Auth::attempt($authentication)) {
      $request->session()->regenerate();
      return redirect()->intended(route('home'));
    } else {
      return back()->withErrors([
        'result' => 'ログインに失敗しました'
      ]);
    }
  }
}
  受け渡されたエラーメッセージは「$errors」変数に格納されていますのでそれらをViewファイル内で出力されるように処理を加えていきます。またエラーがあるかどうかの分岐も「@error」のディレクティブでフォーム入力値のキーを指定することでチェックできるので便利ですね。エラーメッセージは「$message」の変数から取得します。 【resources/views/signin/index.blade.php】※一部抜粋
@if($errors->any())
  <p style="color: red;">{{ $errors->first('result') }}</p>
@endif
<form method="post" action="{{ route('signin') }}">
  @csrf
  <div>
    <label>メールアドレス</label>
    <input type="email" name="email" placeholder="例)info@example.com" value="{{ old('email') }}">
    @error('email')
      <p style="color: red;">{{ $message }}</p>
    @enderror
  </div>
  <div>
    <label>パスワード</label>
    <input type="password" name="password">
    @error('password')
      <p style="color: red;">{{ $message }}</p>
    @enderror
  </div>
  <input type="submit" value="サインイン">
</form>
  上記のメールアドレス入力欄ですが、value属性の値に初期値として「old()」メソッドが使われていますが、セッション内にある既に入力された値を保持することができます。このメソッドでは、入力値がある場合にはその入力値を返してくれるので、初期値に保持されている入力値を設定することで、再入力の手間を省くことができます。    
11. ログイン保持機能を追加
最後にログイン保持機能も追加していきます。これでログアウトしない限りログインされたままにしておくことができるようになります。一般的にはログインフォームのところに、チェックボックスを設けておき、チェックをした状態でログインするとログイン状態が保持されるという形が多いようですので、今回もその形で実装していきます。既に作成済みのログインフォームにログイン保持用のチェックボックスを追加します。 【resources/views/signin/index.blade.php】※一部抜粋
@if($errors->any())
  <p style="color: red;">{{ $errors->first('result') }}</p>
@endif
<form method="post" action="{{ route('signin') }}">
  @csrf
  <div>
    <label>メールアドレス</label>
    <input type="email" name="email" placeholder="例)info@example.com" value="{{ old('email') }}">
    @error('email')
      <p style="color: red;">{{ $message }}</p>
    @enderror
  </div>
  <div>
    <label>パスワード</label>
    <input type="password" name="password">
    @error('password')
      <p style="color: red;">{{ $message }}</p>
    @enderror
  </div>
  <div>
    <label>
      <input type="checkbox" name="remember">ログインを継続する
    </label>
  </div>
  <input type="submit" value="サインイン">
</form>
  ログイン保持用のフラグメントの値が追加されるので、Modelファイルを更新していきます。この情報は値として再利用することはないので、パスワード同様に$hiddenの変数に指定しておきます。 【app/Models/AdminUsers.php】※一部抜粋
class AdminUsers extends Authenticatable
{
  use HasFactory;

  protected $fillable = [
    'email',
    'password',
    'name',
  ];

  protected $hidden = [
    'password',
    'remember_token'
  ];
}
  この値はデータベースのテーブル内に情報をもつ形になりますので、データベースの更新が必要となります。そのためにマイグレーションファイルを作成します。
$ docker-compose exec app php laravel/artisan make:migration update_admin_users_table
  作成されたマイグレーションファイルに、ログイン保持機能の値が入るカラムをテーブルに追加する処理を加えていきます。この値はトークンとして文字列で格納されるようになります。 【database/migrations/yyyy_mm_dd_******_update_admin_users_table.php】
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
  public function up()
  {
    Schema::table('admin_users', function (Blueprint $table) {
      $table->string('remember_token')->nullable(true);
    });
  }

  public function down() {}
};
  ログイン用の処理に保持機能の値を使えるように編集していきます。ログイン保持機能の値には、Authファサードの「attempt()」メソッドを使います。第一引数にはログイン認証情報の値が入りますが、第二引数にログイン保持機能の値を格納することでログイン保持ができるようになります。 【app/Http/Controllers/SigninController.php】※一部抜粋
class SigninController extends Controller
{
  ......
  public function signin(Request $request) {
    $authentication = $request->validate(
      [
        'email' => ['required', 'email'],
        'password' => ['required'],
      ],
      [
        'email.required' => 'メールアドレスを入力してください',
        'email.email' => 'メールアドレスの形式が異なります',
        'password.required' => 'パスワードを入力してください',
      ]
    );
    if(Auth::attempt($authentication, $request->has('remember'))) {
      $request->session()->regenerate();
      return redirect()->intended(route('home'));
    } else {
      return back()->withErrors([
        'result' => 'ログインに失敗しました'
      ]);
    }
  }
}
  これで実装自体は完了ですので、マイグレーションをしてテーブルにカラムが作成されているか、そして、ログイン保持の状態でログインすると作成されたカラムにトークン値が入っていることが確認できますね。
| id | email            | password   | name          | created_at          | updated_at          | deleted_at | remember_token |
| 1  | test@example.com | $2y$10$... | サンプルユーザー | 2023-01-07 19:06:59 | 2023-01-07 19:06:59 | NULL        | 7yiJFm91I7B    |
 
  今回はLaravelでアプリケーションのログイン認証機能を作成してみました。今回はとてもシンプルですが、実際にはバリデーションの設定などが必要に応じて加わることになるかと思います。Laravelではログイン認証周りでも専用のメソッドが用意されていたりと、とても手軽に実装できるのがいいですね。   (これまでの内容はこちらの記事から) Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#1:Docker環境構築 Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#2:Laravelのインストール Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#3:静的ページの作成とルーティング Docker+Laravelでバックエンドの開発環境構築とウェブアプリケーション制作#4:ログイン認証ユーザー作成
  • はてなブックマーク
  • Pocket
  • Linkedin
  • Feedly

この記事を書いた人

Twitter

sponserd

    keyword search

    recent posts

    • Twitter
    • Github
    contact usscroll to top
      • Facebook
      • Twitter
      • Github
      • Instagram