こんにちは、いまこです。
もうずいぶん経ちましたが、Laravel5.2、出ましたね。
Multi-Auth、対応しましたね。
とか言うと古参ぽく感じてしまうかもしれませんが、Laravel歴半年も立っていないペーペーです。
というか、5.2からさわり始めた感じになります 笑
そんなLaravelペーペーな僕ですが、
今回はLaravel5.2の認証系機能について、書いていこうと思います。
GitHubにMulti-Authを使用したサンプルアプリをあげたので、
興味がある方はご覧ください。
デフォルト認証機能について
Laravelにはデフォルトで認証機能が備わっております。
下記artisanコマンドを実行するだけで、ルーティング定義やviewの作成等、
デフォルトで使用する認証機能の全てを用意してくれます。
php artisan make:auth
機能
デフォルトでは下記の機能が備わっております(もっとあるかも…)。
- 新規登録
- ログイン
- ログアウト
- バリデート
- パスワードリセット
- Remember Me(ログイン状態維持)
- ログインロック(n回ログインに失敗したらn秒間ログインできない)
気が付きましたか?
そうです、退会処理はないのです!!
なので退会処理は自作する必要があります。
ルーティング
app/Http/routes.phpに下記を記述するだけで、ルーティングの定義を行ってくれます。
// app/Http/routes.php
Route::group(['middleware' => ['web']], function () {
// 認証ルーティング定義
Route::auth();
});
後記しますが、ルーティングを定義するメソッドを実行しています。
実際使用するときはルーティングを修正する必要があると思うので、
Route::auth()は使用しないか、
名前空間を上手く使用して、対応させる必要があると思います。
ためしにやってみる!!的な感じだと非常に便利です。
コントローラー
Laravel5.2をインストールすると、デフォルトで下記コントローラーが作成されています。
- app/Http/Controller/AuthController.php
- app/Http/Congroller/PasswordController.php
こいつらが、Route::auth()で定義したルーティングに対応している感じになります。
Multi-Authについて
複数種類のログインアカウントを管理することが出来る機能です。
例えば、ユーザーと管理者のアカウントを別々に管理する、という感じです。
管理者のみ閲覧可能にする制御等、簡単にすることができます。
基本的には上で説明したLaravelのデフォルト認証機能をベースに、Multi-Authを使用する感じになると思います。
Multi-Authの使い方
- config/auth.phpに認証アカウント設定追加
- 設定したアカウントに対応するテーブル作成
- ルーティング設定
- ルーティングに対応するController/メソッド作成
- View作成
やることはこのくらいになります。
あとはお好みでカスタムしていく感じになります。
auth.phpに設定追加
下記のように追加します。
※デフォルトで記述されている設定は削除し、3種類のアカウント設定を行っています
// config/auth.php
// デフォルト設定
'defaults' => [
'guard' => 'user_accounts',
'passwords' => 'user_accounts',
],
// アカウントの種類
'guards' => [
'admin_accounts' => [
'driver' => 'session',
'provider' => 'admin_accounts',
],
'client_accounts' => [
'driver' => 'session',
'provider' => 'client_accounts',
],
'user_accounts' => [
'driver' => 'session',
'provider' => 'user_accounts',
],
],
// 対応するモデル
'providers' => [
'admin_accounts' => [
'driver' => 'eloquent',
'model' => App\AdminAccount::class,
],
'client_accounts' => [
'driver' => 'eloquent',
'model' => App\ClientAccount::class,
],
'user_accounts' => [
'driver' => 'eloquent',
'model' => App\UserAccount::class,
],
],
// パスワードリセット時設定
'passwords' => [
'admin_accounts' => [
'provider' => 'admin_accounts',
'email' => 'admin_accounts.emails.password',
'table' => 'admin_account_password_resets',
'expire' => 60,
],
'client_accounts' => [
'provider' => 'client_accounts',
'email' => 'client_accounts.emails.password',
'table' => 'client_account_password_resets',
'expire' => 60,
],
'user_accounts' => [
'provider' => 'user_accounts',
'email' => 'user_accounts.emails.password',
'table' => 'user_account_password_resets',
'expire' => 60,
],
],
テーブル作成
対応するテーブルを作成します。
今回のサンプルだと下記テーブルを作成しています。
- admin_accounts
- client_accounts
- user_accounts
- admin_account_password_resets
- client_account_password_resets
- user_account_password_resets
基本的に1つのアカウントに対して
- 会員情報テーブル
- パスワードリセット用テーブル(使用するなら)
が必要となります。
ルーティング
先ほど説明したRoute::auth()が何をしているか見てみましょう。
// Illuminate/Routing/Router.php
public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\AuthController@showLoginForm');
$this->post('login', 'Auth\AuthController@login');
$this->get('logout', 'Auth\AuthController@logout');
// Registration Routes...
$this->get('register', 'Auth\AuthController@showRegistrationForm');
$this->post('register', 'Auth\AuthController@register');
// Password Reset Routes...
$this->get('password/reset/{token?}', 'Auth\PasswordController@showResetForm');
$this->post('password/email', 'Auth\PasswordController@sendResetLinkEmail');
$this->post('password/reset', 'Auth\PasswordController@reset');
}
ただルーティングを定義しているだけですね。
なのでこれをベースとし、各認証のルーティングを定義します。
管理者(admin_accounts)アカウントを例とします。
//app/Http/routes.php
Route::group(['prefix' => 'admin'], function() {
// Authentication Routes...
$this->get('login', 'AdminAccount\AuthController@showLoginForm');
$this->post('login', 'AdminAccount\AuthController@login');
$this->get('logout', 'AdminAccount\AuthController@logout');
// Registration Routes...
$this->get('register', 'AdminAccount\AuthController@showRegistrationForm');
$this->post('register', 'AdminAccount\AuthController@register');
// Password Reset Routes...
$this->get('password/reset/{token?}', 'AdminAccount\PasswordController@showResetForm');
$this->post('password/email', 'AdminAccount\PasswordController@sendResetLinkEmail');
$this->post('password/reset', 'AdminAccount\PasswordController@reset');
}
こんな感じになります。
コントローラー
デフォルトの認証用コントローラーをコピーして、ルーティングに対応させるため、下記ディレクトリ構造にしています。
※admin_accountsを例とします
app -- Http -- Controller -- AdminAccount -- AuthController.php
-- PasswordController.php
AuthController.php修正
以下を行います
- 名前空間修正
- モデル修正
- プロパティ設定
- テーブル修正
// app/Http/Controller/AdminAccount/AuthController.php
// 名前空間修正
namespace App\Http\Controllers\AdminAccount;
// モデル修正
use App\AdminAccount;
・
・
・
// プロパティ設定
protected $guard = 'admin_accounts'; // 使用するguard名(デフォルトはauth.phpのデフォルト設定してあるguard)
protected $registerView = 'admin_accounts.register'; // 新規登録画面のview(デフォルトは「auth.register」)
protected $loginView = 'admin_accounts.login'; // ログインページのview(デフォルトは「auth.authenticate」)
protected $redirectTo = '/admin/index'; // ログイン後のリダイレクト先(デフォルトは「/home」)
protected $redirectAfterLogout = '/'; // ログアウト後のリダイレクト先(デフォルトは「/」)
protected $username = 'email'; // 認証用のカラム(デフォルトは「email」)
protected $maxLoginAttempts = 5; // ログインスロットルとなるまで最高のログイン失敗回数(デフォルトは「5」)
protected $lockoutTime = 60; // ログインスロットルとなってからの待ち秒数(デフォルトは60)
・
・
・
// テーブル修正
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:admin_accounts',
'password' => 'required|confirmed|min:6',
]);
}
・
・
・
// モデル修正
protected function create(array $data)
{
return AdminAccount::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
guardを宣言しなければ、auth.phpのdefault設定されてあるguardを使用することになってしまうので、気を付けてください。
PasswordController.php修正
- プロパティ設定
// app/Http/Controller/AdminAccount/PasswordController.php
protected $guard = 'admin_accounts'; // auth.guard設定(デフォルトはauth.phpでデフォルト設定したguard)
protected $broker = 'admin_accounts'; // auth.passwords設定('デフォルトはauth.phpでデフォルト設定したpasswords')
protected $linkRequestView = 'admin_accounts.passwords.email'; // メールアドレス入力view(デフォルトは「auth.passwords.email」)
protected $resetView = 'admin_accounts.passwords.reset'; // パスワードリセットページview(デフォルトは「auth.passwords.reset」or「auth.reset」)
protected $subject = 'Password Reset'; // リセットリンクメールの件名(デフォルトは「Your Password Reset Link」)
protected $redirectTo = '/'; // パスワード変更後のリダイレクト先(デフォルトは「/home」)
broderも宣言しなければ、デフォルト設定が使用されます。
View作成
あとは対応するViewを作成すれば、とりあえず使えるようになると思います。
※サンプルアプリではphp artisan make:authコマンドで生成されたviewをコピーして使用しています
ディレクトリ構造例
resources -- views -- admin_accounts -- viewファイル
client_accounts -- viewファイル
user_accounts -- viewファイル
認証系処理をカスタムする場合
処理を追加したりしたい場合、1から認証処理を作成してもよいのですが、
せっかくデフォルトで備わっているので(あとめんどくさい)、
デフォルトの機能をカスタムしていけばよいと思います。
AuthController.php
AuthController.phpは下記トレイトをuseし、基本的にはそのメソッドを使用しています。
// app/Http/Controllers/Auth/AuthController.php
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
なのでちょっと処理を変更したい場合は、メソッドをオーバーライドして使用すればOKです。
例えば、ログイン後にログインした日時を記録したい場合、ログイン処理をオーバーライドし、ログイン日時を記録する処理を追加します。
元のメソッド
// Illuminate/Foundation/Auth/AuthenticatesUsers.php
public function login(Request $request)
{
$this->validateLogin($request);
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
return $this->sendFailedLoginResponse($request);
}
メソッドをオーバーライドし、処理追加
// app/Http/Controller/AdminAccount/AuthController.php
public function login(Request $request)
{
$this->validateLogin($request);
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
// ログイン後に現在時間登録(last_login_timeカラムがある前提)
$date = date('Y-m-d H:i:s');
AdminAccount::where('id', Auth::guard($this->guard)->user()->id)->update(['last_login_time' => $date]);
return $this->handleUserWasAuthenticated($request, $throttles);
}
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
return $this->sendFailedLoginResponse($request);
}
PasswordController.php
PasswordController.phpは下記トレイトをuseしています。
// app/Http/Controllers/Auth/PasswordController.php
use ResetsPasswords;
こっちも同じく、メソッドをオーバーライドすればカスタム可能です。
小ネタ
guestミドルウェア
guestミドルウェアは、指定guardがログイン状態の場合、「/」にリダイレクトさせる処理が書かれています。
ログイン状態の時に、ログインフォームを表示させないためですね。
routes.phpでミドルウェアを指定して使用します。
下記だと、管理者でログイン中は、管理者ログインフォームにアクセスしようとしても、「/」にリダイレクトされるようになります。
Route::group(['middleware' => 'guest:admin_accounts'], function() {
$this->get('login', 'AdminAccount\AuthController@showLoginForm');
});
便利な機能なのですが、guardミドルウェアを使用すると、何故か指定されたguardとデフォルトのguardの2回処理を行うようになっています。
つまり、デフォルトguardがログイン状態だと、他のguardもログイン状態とみなされ、ログインフォームにアクセスできなくなってしまいます。
これを解消したい場合は、下記のようにguestミドルウェアをカスタムすればOKです。
app/Http/Middleware/RedirectIfAuthenticated.php
public function handle($request, Closure $next, $guard = null)
{
// $guardが指定されていない場合、認証チェックを行わせない
if ($guard !== null && Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
パスワードリセットについて
パスワードリセット機能を使用するためには、下記カラムが存在するテーブルが必要です。
- token
逆に言うと、このカラムさえあれば大丈夫なので、アカウント毎にテーブルを分けなくてもいいのでは???
と思ったのですが、emailの重複があった場合、上書き保存されてしまうため、アカウントの数だけテーブルが必要でした。
まあでも、例えばカラムにguardを追加して、登録処理をカスタムすればまとめることが出来ると思うので、やり方次第かなと思います。
ちなみにパスワードリセットが完了したら、レコードは削除されます。
Authファサード
認証系の色々なことが出来るファサードです。
ユーザーがログイン中か
if (Auth::guard('guard名')->check()) {
// ログイン中
} else {
// 未ログイン
}
ログインユーザー情報を取得
$user = Auth::guard('guard名')->user();
認証処理
if (Auth::attempt(['email' => '登録されているemail', 'password' => 'ハッシュ化されたパスワード'])) {
// 認証成功
} else {
// 認証失敗
}
等々…
詳しくは公式リファレンス
またはコードを読んでください。
最後に
Laravel5.2の認証機能について触れてみました。
今回説明したことは、あくまで一例ですので、
1から認証機能を作成しても良いですし、
ディレクトリ構造も様々な形があると思います。
言ってみれば自由です。
自由に、作りやすいように作ってください。