
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、Laravel 8.x
がリリースされてしばらく経ちましたが、やはり驚きとともに迎えられたのがログインパッケージJetstream
ではないでしょうか。
というのも、Jetstream
はインストールするだけでスマホのアプリと連携した2段階認証を実装していたりと、とても多機能だからですね。
そして、今回の記事はこのJetstream
にある機能をつけてみたくなったことが始まりでした。
それは・・・・・・・
マルチ認証
です。
ここで言うマルチ認証とは、一般的なusers
テーブルにrole
データもたせたものではなく、全く別のモデルをいくつか使った「本格的な」マルチ認証です。
この方式のメリットとしては、各モデルで保持するデータが別でもOKという点です。(例えば、芸人ユーザーなら「師匠の名前」、ミュージシャンなら「担当パート」という具合に専用のデータを持たせていいということになります)
そこで
今回は複数モデルを使ったマルチ認証を実装する方法をご紹介します。
ぜひ皆さんのお役に立てると嬉しいです
(最後に実際に開発したソースコード一式をダウンロードできますよ)
「見られると緊張するので、
撮影しながらピアノ練習中!」
目次 [非表示]
前提として
すでにLaravel
にログイン機能Jetstream
がインストールされていることが前提です。
まだの方は以下のURLを参考にしてみてください。
Laravel8.x以降でログイン機能をインストールする方法
やりたいこと
少しでもイメージしやすくしたいので、今回は以下の3つのモデルでログインするように実装してみます。
- Comedian: 芸人
- Musician: ミュージシャン
- Athlete: アスリート
では、実際にやっていきましょう!
モデル&マイグレーションをつくる
まずはじめに、先ほど紹介した3タイプのユーザー(芸人、ミュージシャン、アスリート)のモデルとマイグレーションをつくっていきます。
以下のコマンドを実行してください。
php artisan make:model Comedian -m
php artisan make:model Musician -m
php artisan make:model Athlete -m
モデルを設定する
すると、モデルのファイルが作成されていますので、ログインに対応させるため、それぞれAuthenticatable
をextends
(継承)するように変更します。
app/Models/Comedian.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; //
ここを追加しました
class Comedian extends Authenticatable //
ここを変更しました
{
// 省略
app/Models/Musician.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable; //
ここを追加しました
class Musician extends Authenticatable //
ここを変更しました
{
// 省略
app/Models/Athlete.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable; //
ここを追加しました
class Athlete extends Authenticatable //
ここを変更しました
{
// 省略
マイグレーションをつくる
先ほどのコマンドで同時にマイグレーションも作成されています(-m
をつけたからです)ので、こちらも設定していきましょう。
【芸人さんのテーブル】
database/migrations/****_**_**_******_create_comedians_table.php
<?php
// 省略
class CreateComediansTable extends Migration
{
public function up()
{
Schema::create('comedians', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('名前');
$table->string('email')->unique()->comment('メールアドレス');
$table->timestamp('email_verified_at')->nullable()->comment('メール認証日時');
$table->string('password')->comment('パスワード');
$table->rememberToken()->comment('ログイン省略トークン');
// 芸人さんの専用データ
$table->string('tv_programs')->nullable()->comment('代表的なテレビ番組');
$table->string('master')->nullable()->comment('師匠');
$table->timestamps();
});
}
【ミュージシャンのテーブル】
database/migrations/****_**_**_******_create_musicians_table.php
<?php
// 省略
class CreateMusiciansTable extends Migration
{
public function up()
{
Schema::create('musicians', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('名前');
$table->string('email')->unique()->comment('メールアドレス');
$table->timestamp('email_verified_at')->nullable()->comment('メール認証日時');
$table->string('password')->comment('パスワード');
$table->rememberToken()->comment('ログイン省略トークン');
// ミュージシャン専用データ
$table->string('instruments')->comment('楽器');
$table->string('genre')->comment('音楽ジャンル');
$table->timestamps();
});
}
【アスリートのテーブル】
database/migrations/****_**_**_******_create_athletes_table.php
<?php
// 省略
class CreateAthletesTable extends Migration
{
public function up()
{
Schema::create('athletes', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('名前');
$table->string('email')->unique()->comment('メールアドレス');
$table->timestamp('email_verified_at')->nullable()->comment('メール認証日時');
$table->string('password')->comment('パスワード');
$table->rememberToken()->comment('ログイン省略トークン');
// アスリート専用データ
$table->string('sports_type')->comment('スポーツの種類');
$table->boolean('is_active')->comment('現役 or 引退済み');
$table->timestamps();
});
}
では、これで一旦マイグレーションを実行してDBテーブルを作成しておきましょう。
以下のコマンドを実行してください。
php artisan migrate
するとテーブルは以下のようになります。
ここでお気づきかもしれませんが、これらのテーブル構成は3つ全て同じにする必要はありません。(ただし、ログインに必要なメールアドレスとパスワードは用意する必要があります)
ここが冒頭でも書いたマルチ認証のメリットです
もちろん、通常どおりUser
モデルだけを使い、role
の中身でユーザータイプを分ける方法もありますが、その場合は保存する内容が同じか似ている場合に向いているので、どちらを選択するかは保存するデータの中身で決めるといいでしょう。
テストデータをつくる
では、まだ3つのテーブルは空のままですので、作業をしやすいようにそれぞれ1件ずつテストデータを追加しましょう。
以下のコマンドを実行してください。
php artisan make:seed MultiAuthTableSeeder
するとSeederファイル
(テストデータの設定ファイル)が作成されるので中身を以下のように変更してください。
database/seeders/MultiAuthTableSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Athlete;
use App\Models\Comedian;
use App\Models\Musician;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class MultiAuthTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// 芸人
$init_comedians = [
[
'name' => '明石家さんま',
'email' => 'sanma@example.com',
'password' => 'secret',
'tv_programs' => 'ホンマでっかTV、お笑い向上委員会',
'master' => '笑福亭松之助'
],
// ここに追加できます
];
foreach($init_comedians as $init_comedian) {
$comedian = new Comedian();
$comedian->name = $init_comedian['name'];
$comedian->email = $init_comedian['email'];
$comedian->password = Hash::make($init_comedian['password']);
$comedian->tv_programs = $init_comedian['tv_programs'];
$comedian->master = $init_comedian['master'];
$comedian->save();
}
// ミュージシャン
$init_musicians = [
[
'name' => 'Yoshiki',
'email' => 'yoshiki@example.com',
'password' => 'secret',
'instruments' => 'ピアノ、ドラム',
'genre' => 'ロック、クラシック'
],
// ここに追加できます
];
foreach($init_musicians as $init_musician) {
$musician = new Musician();
$musician->name = $init_musician['name'];
$musician->email = $init_musician['email'];
$musician->password = Hash::make($init_musician['password']);
$musician->instruments = $init_musician['instruments'];
$musician->genre = $init_musician['genre'];
$musician->save();
}
// アスリート
$init_athletes = [
[
'name' => 'ヒクソン・グレイシー',
'email' => 'rickson@example.com',
'password' => 'secret',
'sports_type' => '柔術',
'is_active' => false
],
// ここに追加できます
];
foreach($init_athletes as $init_athlete) {
$athlete = new Athlete();
$athlete->name = $init_athlete['name'];
$athlete->email = $init_athlete['email'];
$athlete->password = Hash::make($init_athlete['password']);
$athlete->sports_type = $init_athlete['sports_type'];
$athlete->is_active = $init_athlete['is_active'];
$athlete->save();
}
}
}
※ わかりにくくなるので敬称を省略させていただきました。(見てないでしょうが、)ご本人様、ファンの方々どうぞご了承ください。また、年代がバレてしまうテストデータですみません
※ なお、通常はComedian
、Musician
、Athlete
ごとにSeederファイル
をつくる方がいいかもしれませんが、今回はテストなので1ファイルにまとめました。また、Laravel 8.x
からはfactory
が使いやすくなっていますので、そちらが気になる方は以下のページも参考にしてみてください。
Laravel 8.x の新機能・変更点のまとめ : Factory の構造が変更になる
では、このSeederファイル
を登録して有効にしておきましょう。
database/seeders/DatabaseSeeder.php
<?php
// 省略
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call(MultiAuthTableSeeder::class); //
ここを追加しました
}
}
では、これでテストデータを追加してみましょう。
以下のコマンドを実行してください。
php artisan migrate:fresh --seed
するとテーブルの中身は次のようになります。
各モデルでログインできるように設定する
では、先ほど作成した以下3つのモデルを実際にログインで使えるように実装してみましょう。
コンフィグファイルを変更する
コンフィグファイルでログインの設定を変更しましょう。
config/auth.php
<?php
return [
// 省略
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
//
ここを追加しました
'comedians' => [
'driver' => 'session',
'provider' => 'comedians',
],
'musicians' => [
'driver' => 'session',
'provider' => 'musicians',
],
'athletes' => [
'driver' => 'session',
'provider' => 'athletes',
]
],
// 省略
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
//
ここを追加しました
'comedians' => [
'driver' => 'eloquent',
'model' => App\Models\Comedian::class,
],
'musicians' => [
'driver' => 'eloquent',
'model' => App\Models\Musician::class,
],
'athletes' => [
'driver' => 'eloquent',
'model' => App\Models\Athlete::class,
]
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
// 省略
コントローラーをつくる
次にマルチ認証のためのコントローラーをつくります。
以下のコマンドを実行してください。
artisan make:controller MultiAuthController
するとファイルが作成されるので、中身を以下のように変更してください。
app/Http/Controllers/MultiAuthController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class MultiAuthController extends Controller
{
public function showLoginForm() {
return view('multi_auth.login');
}
public function login(Request $request) {
$credentials = $request->only(['email', 'password']);
$guard = $request->guard;
if(\Auth::guard($guard)->attempt($credentials)) {
return redirect($guard .'/dashboard'); // ログインしたらリダイレクト
}
return back()->withErrors([
'auth' => ['認証に失敗しました']
]);
}
}
中身としては、showLoginForm()
がブラウザでメールアドレスやパスワードを入力するページ、そして、login()
はその送信先になります。
ビューをつくる
先ほどのshowLoginForm()
の中で指定したビューをつくっていきます。
以下のファイルを作成してください。
resources/views/multi_auth/login.blade.php
<html>
<head>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<form method="POST" action="multi_login">
@csrf
<div class="p-3">
@error('auth')
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-3">
⚠ {{ $message }}
</div>
@enderror
<label class="block">メールアドレス</label>
<input class="border rounded mb-3 px-2 py-1" type="text" name="email">
<label class="block">パスワード</label>
<input class="border rounded mb-3 px-2 py-1" type="password" name="password">
<label class="block">ユーザータイプ</label>
<select name="guard" class="border rounded px-2 py-1 mb-5">
<option value="">▼選択してください</option>
<option value="comedians">芸人</option>
<option value="musicians">ミュージシャン</option>
<option value="athletes">アスリート</option>
</select>
<br>
<button class="bg-blue-500 text-white rounded px-3 py-2" type="submit">ログイン</button>
</div>
</form>
</body>
</html>
※なお、CSS
はいま勢いがある(と思われる)Tailwind CSS
を使っています
ルートをつくる
続いてルートです。
以下の行を追加してください。
routes/web.php
<?php
// 省略
// マルチ認証
// ログイン
Route::get('multi_login', [\App\Http\Controllers\MultiAuthController::class, 'showLoginForm']);
Route::post('multi_login', [\App\Http\Controllers\MultiAuthController::class, 'login']);
// ログアウト
Route::get('multi_login/logout', [\App\Http\Controllers\MultiAuthController::class, 'logout']);
// ログイン後のページ
Route::prefix('comedians')->middleware('auth:comedians')->group(function(){
Route::get('dashboard', function(){ return '芸人でログイン完了'; });
});
Route::prefix('musicians')->middleware('auth:musicians')->group(function(){
Route::get('dashboard', function(){ return 'ミュージシャンでログイン完了'; });
});
Route::prefix('athletes')->middleware('auth:athletes')->group(function(){
Route::get('dashboard', function(){ return 'アスリートでログイン完了'; });
});
ちなみに、ミドルウェアのauth
は:
に続けてコンフィグで設定したユーザー名を書くと、自動的にそのユーザーのログインを判別してくれるようになります。
ログインしていないときの強制リダイレクトをつくる
現時点でもミドルウェアが効いているので、ログインしていないのにログインページへアクセスしようとすると強制リダイレクトされることになります。ただ、移動先が通常のログインページになっているので、これを変更します。
app/Http/Middleware/Authenticate.php
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Str;
class Authenticate extends Middleware
{
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
$uri = $request->path();
// URIが以下3つから始まる場合
if(Str::startsWith($uri, ['comedians/', 'musicians/', 'athletes/'])) {
return 'multi_login';
}
return route('login');
}
}
}
これで全て完了です!
テストしてみる
では、実際にテストしてみましょう
まずは、私の大好きな芸人さんの「明石家さんま」さんでログインです。
「http://******/multi_login」にブラウザでアクセスし、メールアドレス、パスワード、そして、ユーザータイプを「芸人」にして送信します。
すると・・・・・・
うまくログインができました
では、この状態で(実際にはログインしていない)ミュージシャンのページ「http://******/musicians/dashboard」にアクセスしてみましょう。
すると・・・・・・・
はい
ミュージシャンとしてはログインしていないので、強制リダイレクトされました。
では最後に、メールアドレス&パスワードは正しいけれど、ユーザータイプをわざと間違えてログインしてみましょう。
すると・・・・・・・・
エラーメッセージが表示されました。
成功です
ダウンロードする
今回実際に開発した内容を以下からダウンロードすることができます。
【Laravel Jetstream】複数モデルでログインできるようにする※ただし、マイグレーションなどはご自身で実行してください。
おわりに
ということで今回はLaravel 8.x
のログインパッケージJetstream
を使ってマルチ認証を実装してみました。
ところどころLaravel 8.x
の書き方になっていますが、根本はこれまでと同じですので応用すれば過去バージョンにも参考になると思います。
なお、冒頭で書いたrole
を使ったログインは以下のURLをご覧ください。
シンプル!Laravel5.6で「権限つき」ログインさせる方法
どちらかと言えば、私はこちらの方が好きですが開発する内容によっては使い分けをしています。
ぜひ皆さんもやってみてくださいね。
ではでは〜
「電子レンジでパスタが
茹でられるって感動ですね」