Laravel で Eloquent ORM を使わないで認証する方法
2021/08/10, last updated 2022/08/03 - ~5 Minutes
ORM(オブジェクトリレーショナルマッピング) でなく、SQL を使う、という場面は多々あると思う。 何をやっているのかわかりやすい、デバッグしやすい、パフォーマンスを追求できる、既存の DB がある、といった場面だろうか。
そこで、Laravel で Eloquent ORM でなく MySQL などの RDB(リレーショナルデータベース) に対する SQL クエリを使って認証する方法について書きたいと思う。 (laravel mysql auth で出てくる Google 検索結果には、本当に、失望してしまった。)
Adding Custom User Providers がそのものであろう。
ただ、そのままだとわかりづらいので、少し解説を加えたいと思う。
データベースには DB facades でアクセスできるようになっているものとする。
テーブルの DB 定義
テーブル名は、example_users とし、以下のカラムが定義されているものとする。
- login_id
- login_password
- user_name
また、login_password には、password_hash 関数で生成した文字列がセットされているものとする。
UserProvider を追加する
次のように UserProvider 実装した ExampleProvider クラスを作る。
この例では、App\Models\ExampleUser に Authenticatable を実装するものとする。
app\Providers\ExampleUserProvider.php
<?php
namespace App\Providers;
use App\Models\ExampleUser;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Facades\DB;
class ExampleUserProvider implements UserProvider
{
public function retrieveById($identifier)
{
$user = DB::table('example_users')->where('login_id', $identifier)->first();
if (empty($user)) {
return null;
} else {
return new ExampleUser($user); // Authenticatable
}
}
public function retrieveByToken($identifier, $token)
{
return null; // 対応しないので null を返す
}
public function updateRememberToken(Authenticatable $user, $token)
{
}
public function retrieveByCredentials(array $credentials)
{
$user = DB::table('example_users')->where(
'login_id', // テーブルカラム名
$credentials['id'] // 入力値など
)->first();
if (empty($user)) {
return null;
} else {
return new ExampleUser($user); // Authenticatable
}
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// boolean 値を返す
return password_verify(
$credentials['password'], // 入力値
$user->getAuthPassword() // Authenticatable にセットされたハッシュ化されたパスワードなど
);
}
}
Authenticatable を追加する
次のように Authenticatable を実装したクラスを作る。
app\Models\ExampleUser.php
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\Authenticatable;
class ExampleUser implements Authenticatable
{
private $login_id;
private $login_password;
private $user_name;
public function __construct($obj)
{
$this->login_id = $obj->login_id;
$this->login_password = $obj->login_password;
$this->user_name = $obj->user_name;
}
public function getAuthIdentifierName()
{
return "login_id";
}
public function getAuthIdentifier()
{
return $this->{$this->getAuthIdentifierName()};
}
public function getAuthPassword()
{
return $this->login_password;
}
public function getRememberToken()
{
return null; // 対応しないので null を返す。
}
public function setRememberToken($value)
{
return; // 対応しないので、何もしない。
}
public function getRememberTokenName()
{
return null; // 対応しないので null を返す。
}
// ユーザー名を表示するために拡張
public function getUserName()
{
return $this->user_name;
}
}
UserProvider を登録する
app/Providers/AuthServiceProvider.php
冒頭の use に以下を追加する。
use Illuminate\Support\Facades\Auth;
boot 関数の末尾に以下を追加する。
Auth::provider('example', function($app, array $config) {
return new ExampleUserProvider();
});
config/auth.php
providers を以下のようにする。
'providers' => [
'users' => [
'driver' => 'example', // Auth::provider の第一引数
],
],
これで、Auth facades が使えるようになっているはずだ。
検証
Manually Authenticating Users を参考に検証してみたいと思う。
プロジェクト生成
Installtion Via Composer にあるように、以下を実行してアプリの雛形を作る。
composer create-project laravel/laravel example-app
cd example-app
php artisan serve
特に問題なければ、ブラウザで http://127.0.0.1:8000 にアクセスすると、何か表示されるはずだ。
DB 設定
mysql の設定がめんどうなので、sqlite を使ってみる。
SQLite Configuration や、 config/database.php を参考に設定する。
.env
#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=laravel
#DB_USERNAME=root
#DB_PASSWORD=
DB_CONNECTION=sqlite
これで、database/database.sqlite が参照されるようになる。
touch database/database.sqlite
などで空ファイルを作って、
php artisan tinker
DB::statement('create table example_users(login_id, login_password, user_name)');
DB::table('example_users')->insert(['login_id' => 'kuromame', 'login_password' => password_hash('mya', PASSWORD_DEFAULT), 'user_name' => 'ku-tan']);
のようにしてデータを作っておく。 もちろん、sqlite3 コマンドを使って作ってもよい。
DB::table('example_users')->get()
を打つと、登録されていることを確認できる。
シェルからは、
quit
で抜けることができる。
routes/web.php
既存の Route::get(’/’) 〜 は削除し、以下を追加する。
Route::get('/', [App\Http\Controllers\LoginController::class, 'onGet'])->name('login');
Route::post('/', [App\Http\Controllers\LoginController::class, 'onPost']);
Route::get('/logout', function(Request $request) {
Auth::logout();
session()->invalidate();
session()->regenerateToken();
return redirect()->route('login');
})->name('logout');
Route::middleware(['auth'])->group(function() {
Route::get('/dashboard', function() {
return view('dashboard', // resources/views/dashboard.blade.php を描画
array(
'user_name' => Auth::user()->getUserName()
));
})->name('dashboard');
});
app/Http/Controllers/LoginController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class LoginController extends Controller
{
public function onGet(Request $request)
{
return view('login'); // resources/views/login.blade.php を描画
}
public function onPost(Request $request)
{
$credentials = $request->validate([
'id' => 'required',
'password' => 'required'
]);
// validate ok なら、値は $credentials array に格納される。
// validate ng の場合、この例の場合は、前画面にリダイレクトされる
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard');
}
return back()->withErrors([
'other' => 'ID、または、パスワードが違います。'
]);
}
}
resources/views/login.blade.php
<html>
<head>
</head>
<body>
<form method="post">
@csrf
<label for="id">ID</label>
<input type="text" id="id" name="id">
@error('id')
{{ $message }}
@enderror
<label for="password">パスワード</label>
<input type="password" id="password" name="password">
@error('password')
{{ $message }}
@enderror
@error('other')
{{ $message }}
@enderror
<input type="submit" value="ログイン">
</form>
</body>
</html>
resources/views/dashboard.blade.php
<html>
<head>
</head>
<body>
<p>Hello, {{ $user_name }}!</p>
<a href="{{ url('logout') }}">LOGOUT</a>
</body>
</html>
前述した以下のファイルを追加する。
- app\Providers\ExampleUserProvider.php
- app\Models\ExampleUser.php
以上の変更をした後、再度ブラウザで http://127.0.0.1:8000 にアクセスする。
ID: kuromame、パスワード: mya でログインすると、DB テーブルで認証ができるようになっていることがわかる。
laravel 関連書籍ランキング
2021/11/30 PA-API で検索
- 速習 Laravel 6 速習シリーズ 【ランキング 8846 位】
- 動かして学ぶ!Laravel開発入門 【ランキング 9531 位】
- PHPフレームワークLaravel Webアプリケーション開発 バージョン8.x対応 【ランキング 21273 位】
- 1週間で基礎から学ぶLaravel入門 【ランキング 25145 位】
- PHPフレームワーク Laravel入門 第2版 【ランキング 48582 位】
- PHPフレームワーク Laravel実践開発 【ランキング 87658 位】
- PHPフレームワークLaravel Webアプリケーション開発 【ランキング 193843 位】
- LaravelでStripeを使った決済処理付き簡易ファッションECサイトを作ろう!: Laravelと決済プラットフォームであるStripeを使用して、ファッションECサイトの作り方を学ぶ (Techpitブックス) 【ランキング 216636 位】
- Laravelエキスパート養成読本 [モダンな開発を実現するPHPフレームワーク!] 【ランキング 324807 位】