Laravel/Lumen 用の JWT 認証パッケージである tymon/jwt-auth では、発行した有効なトークンを保持するのではなく、ログアウトしたりリフレッシュされた古いトークンをブラックリストという形で保存しておく。デフォルトではキャッシュに保存されるので、Redis などで共有していないとスケールアウトできない。ということで、データベースに保存してみる。
環境
- Laravel 6.0
- tymon/jwt-auth 1.0.0-rc.5
手順
テーブル jwt_auth_storage を作成
database/migrations 配下に定義してマイグレーションする。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJwtAuthStorageTable extends Migration
{
public function up(): void
{
Schema::create('jwt_auth_storage', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
$table->timestamp('expires_at')->nullable();
$table->string('key')->index();
$table->string('value');
});
}
public function down(): void
{
Schema::dropIfExists('jwt_auth_storage');
}
}
jwt_auth_storage テーブルの Eloquent Model を作成
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class JwtAuthStorage extends Model
{
protected $table = 'jwt_auth_storage';
protected $fillable = [
'key', 'value', 'expires_at',
];
protected $dates = ['expires_at'];
}
Storage を継承したアクセスクラスを作成
Tymon\JWTAuth\Contracts\Providers\Storage を継承し、各メソッドを実装したクラスを作成する。デフォルトでは Tymon\JWTAuth\Providers\Storage\Illuminate::class が使用されるので、これを参考にする。
<?php
namespace App\Repository;
use App\Model\JwtAuthStorage;
use DateTime;
use Tymon\JWTAuth\Contracts\Providers\Storage;
class JwtAuthStorageRepository implements Storage
{
protected $jwtAuthStorageModel;
public function __construct(JwtAuthStorage $jwtAuthStorageModel)
{
$this->jwtAuthStorageModel = $jwtAuthStorageModel;
}
public function add($key, $value, $minutes): void
{
$expiresAt = (new DateTime('now'))->modify('+ ' . $minutes . ' minutes');
$this->jwtAuthStorageModel->newQuery()
->create([
'key' => $key,
'value' => serialize($value),
'expires_at' => $expiresAt,
]);
}
public function forever($key, $value): void
{
$this->jwtAuthStorageModel->newQuery()
->create([
'key' => $key,
'value' => serialize($value),
]);
}
public function get($key)
{
$now = new DateTime('now');
$data = $this->jwtAuthStorageModel->newQuery()
->where('key', $key)
->where('expires_at', '>', $now)
->orderBy('expires_at', 'desc')
->first();
if ($data) {
return unserialize($data->value);
} else {
return null;
}
}
public function destroy($key): bool
{
return !!$this->jwtAuthStorageModel->newQuery()
->where('key', $key)
->delete();
}
public function flush()
{
}
}
設定ファイルを publish
設定を変更するため、config/jwt.php をプロジェクトに展開する。
$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
設定
設定ファイル(config/jwt.php)の provider.storage に、カスタマイズしたアクセスクラスを指定する。ブラックリストの読み書きはこのクラスを経由しておこなわれる。
<?php
return [
...
'providers' => [
...
'storage' => App\Repository\JwtAuthStorageRepository::class,
],
];