🔑

Laravel8+Redis コマンドでワンタイムトークンの為の

2021/03/14に公開

前提

  • ワンタイムトークンの前段であるトークンの操作処理を作り方を掲載
  • php8
  • redisの操作はphpredis
  • OSはubuntu20
  • タイトルはトークンとしてあるが、以降はシグネチャー(signature) と表現する
  • 掲載するコードは説明不要の最小限の内容且つ実用性のあるもの

特にワンタイムトークンとして利用しなければいけない訳ではなく、削除処理をロジックに組み込まなければ一定期間有効なトークンとして利用可能

作るコマンド

コマンド 効用
admin:signature:issue 発行
admin:signature:validate 有効性の検証
admin:signature:remove 無効化

トークンを操作する処理の作成

bash
mkdir -p app/Repositories/Admin
touch app/Repositories/Admin/SignatureRepositoryInterface.php
touch app/Repositories/Admin/SignatureRepository.php
mkdir -p app/Services/Admin
touch app/Services/Admin/AccountServiceInterface.php
touch app/Services/Admin/AccountService.php

シグネチャの保存・取得・削除のみを提供する処理

app/Repositories/Admin/SignatureRepositoryInterface.php
<?php
declare(strict_types=1);
namespace App\Repositories\Admin;

use Illuminate\Support\Facades\Redis;

interface SignatureRepositoryInterface{
    public function save(string $signature, int $lifetimeSeconds): bool;
    public function get(string $signature): ?string;
    public function delete(string $signature): void;   
}
app/Repositories/Admin/SignatureRepository.php
<?php
declare(strict_types=1);
namespace App\Repositories\Admin;

use Illuminate\Support\Facades\Redis;
use Carbon\Carbon;
use App\Repositories\Admin\SignatureRepositoryInterface;

class SignatureRepository implements SignatureRepositoryInterface{
    
    public function save(string $signature, $lifetimeSeconds): bool{
        return Redis::set($signature
                        , Carbon::now()->addSeconds($lifetimeSeconds)->timestamp
                        , 'ex'
                        , $lifetimeSeconds
                        , 'nx');
    }
    
    public function get(string $signature): ?string{
        return Redis::get($signature);
    }
    
    public function delete(string $signature): void{
        Redis::command('del', [$signature]);
    }
}

SignatureRepositoryをサービスコンテナにバインド

artisan
php artisan make:provider RepositoryServiceProvider
app/Providers/RepositoryServiceProvider.php
use App\Repositories\Admin\SignatureRepositoryInterface;
use App\Repositories\Admin\SignatureRepository;

    public function register()
    {
        $this->app->bind(SignatureRepositoryInterface::class, SignatureRepository::class);
    }

Signatureの発行・検証・無効化を提供する処理

app/Services/Admin/AccountServiceInterface.php
<?php
declare(strict_types=1);
namespace App\Services\Admin;

use Illuminate\Support\Facades\Redis;

interface AccountServiceInterface{

    public function issueSignature(int $expired): ?string;
    public function validateSignature(string $signature): bool;
    public function revokeSignature(string $signature): void;

}
app/Services/Admin/AccountService.php
<?php
declare(strict_types=1);
namespace App\Services\Admin;

use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use App\Services\Admin\AccountServiceInterface;
use App\Repositories\Admin\SignatureRepositoryInterface;

class AccountService implements AccountServiceInterface{

    private SignatureRepositoryInterface $signatureRepository;

    public function __construct(SignatureRepositoryInterface $signatureRepository){
        $this->signatureRepository = $signatureRepository;
    }

    public function issueSignature(int $expired): ?string{
        $signature = Str::random(64);
        return match($this->signatureRepository->save($signature, $expired)){
            true => $signature
            ,false => null
        };
    }
    public function validateSignature(string $signature): bool{
        return match($this->signatureRepository->get($signature)){
            null => false
            ,default => true            
        };
    }
    public function revokeSignature(string $signature): void{
        $this->signatureRepository->delete($signature);
    }
}

コマンドの作成

発行コマンド

artisan
php artisan make:command Admin/IssueSignatureCommand
app/Console/Commands/Admin/IssueSignatureCommand.php
<?php
namespace App\Console\Commands\Admin;

use Illuminate\Console\Command;
use App\Services\Admin\AccountService;

class IssueSignatureCommand extends Command{
    protected $signature = 'admin:signature:issue 
                            {expired=1800 : 有効期限(秒)指定}';
			    
    protected $description = 'ワンタイムキーを発行する';
    
    public function __construct(){
        parent::__construct();
    }
    
    public function handle(AccountService $accountService){
        $expired = $this->argument('expired');
        $result = $accountService->issueSignature($expired);
        match($result){
            null => $this->error('Issue failure. Please retry issue signature')
            ,default => (function($parent, $result)  {
                            $parent->info('Signature issued!');
                            $parent->line($result);
                        })($this, $result)
        };
    }
}

発行コマンドの動作確認

  • ヘルプの表示
artisan
php artisan admin:signature:issue -h
Description:
  ワンタイムキーを発行する

Usage:
  admin:signature:issue [<expired>]

Arguments:
  expired               有効期限(秒)指定 [default: "1800"]
  • 発行
artisan
php artisan admin:signature:issue 3000
Signature issued!
G2ZmtPVG0jNtACnwcGm36cf9egVEH4FYkUJ3sAPzULTWkPgrRZYU1SUB31LtXFi7

検証コマンド

artisan
php artisan make:command Admin/ValidateSignatureCommand
app/Console/Commands/Admin/ValidateSignatureCommand.php
<?php

namespace App\Console\Commands\Admin;

use Illuminate\Console\Command;
use App\Services\Admin\AccountService;

class ValidateSignatureCommand extends Command{
    protected $signature = 'admin:signature:validate
                            {signature : 有効性の確認をするワンタイムキー}';
			    
    protected $description = 'ワンタイムキーの有効性を確認する';
    
    public function __construct(){
        parent::__construct();
    }
    
    public function handle(AccountService $accountService){
        match($accountService->validateSignature($this->argument('signature'))){
            true => $this->info('Signature is valid')
            ,default => $this->error('Signature is invalid')
        };
    }
}

検証コマンドの動作確認

  • ヘルプ表示
artisan
php artisan admin:signature:validate -h
Description:
  ワンタイムキーの有効性を確認する

Usage:
  admin:signature:validate <signature>

Arguments:
  signature             有効性の確認をするワンタイムキー
  • 有効状態の検証
artisan
php artisan admin:signature:validate G2ZmtPVG0jNtACnwcGm36cf9egVEH4FYkUJ3sAPzULTWkPgrRZYU1SUB31LtXFi7
Signature is valid

無効化コマンド

artisan
php artisan make:command Admin/RevokeSignatureCommand
app/Console/Commands/Admin/RevokeSignatureCommand.php
<?php
namespace App\Console\Commands\Admin;

use Illuminate\Console\Command;
use App\Services\Admin\AccountService;

class RevokeSignatureCommand extends Command{
    protected $signature = 'admin:signature:revoke
                            {signature : 無効化するワンタイムキー}';
			    
    protected $description = 'ワンタイムキーを無効化する';

    public function __construct(){
        parent::__construct();
    }

    public function handle(AccountService $accountService){
        $this->line('Revoke signature: '.$this->argument('signature'));
        $accountService->revokeSignature($this->argument('signature'));
        $this->info('Signature revoked.');
    }
}

-ヘルプの表示

artisan
php artisan admin:signature:revoke -h
Description:
  ワンタイムキーを無効化する

Usage:
  admin:signature:revoke <signature>

Arguments:
  signature             無効化するワンタイムキー
  • 無効化
artisan
php artisan admin:signature:revoke G2ZmtPVG0jNtACnwcGm36cf9egVEH4FYkUJ3sAPzULTWkPgrRZYU1SUB31LtXFi7
Revoke signature: G2ZmtPVG0jNtACnwcGm36cf9egVEH4FYkUJ3sAPzULTWkPgrRZYU1SUB31LtXFi7
Signature revoked.

無効状態のの検証

artisan
php artisan admin:signature:validate G2ZmtPVG0jNtACnwcGm36cf9egVEH4FYkUJ3sAPzULTWkPgrRZYU1SUB31LtXFi7
Signature is invalid

Discussion