API

Laravel6 jwt-auth トークンエラーハンドリング Middleware

Laravel

Laravelのミドルウェア機能を利用して実装していきます😊

LaravelのMiddlewareは

  • リクエストデータ → Middleware → コントローラ → モデル → コントローラ → レスポンス

 

トークンの検証用Middleware CheckToken

 

ミドルウェアの作成

$ php artisan make:middleware CheckRefreshToken

 

ミドルウェアの実装

app/Http/Middleware/CheckRefreshToken.php

<?php

namespace App\Http\Middleware;

use Closure;

class CheckToken
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}

雛形ができたな!

 

 

カスタマイズしていく

app/Http/Middleware/CheckRefreshToken.php

<?php

namespace App\Http\Middleware;

use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Illuminate\Support\Facades\Config;

class CheckRefreshtoken
{
    /**
     * リクエストのヘッダーのトークンを検証
     *
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $string = $token = JWTAuth::getToken();

        // ヘッダーにAuthorizationが存在するかをチェック
        if ((!$request->header('Authorization'))) {

            return response()->json([
                'error' => Config::get('error.headerAuthorizationMissing')
            ]);
        }

        try {
            // トークンに含まれているユーザは存在するかをチェック
            if (!$user = JWTAuth::parseToken()->authenticate()) {
                return response()->json([
                    'error' => Config::get('error.invalidToken')
                ]);
            }
        } catch (TokenInvalidException $e) {
            // 無効なリフレッシュトークンによるエラー
            return response()->json([
                'error' => Config::get('error.invalidRefreshToken')
            ]);
        } catch (TokenExpiredException $e) {
            // リフレッシュトークンの有効期限切れによるエラー
            return response()->json([
                'error' => Config::get('error.expiredRefreshToken')
            ]);
        } catch (JWTException $e) {
            // その他の原因によるトークンのエラー
            return response()->json([
                'error' => Config::get('error.tokenSomethingWentWrongError')
            ]);
        }
        // コントローラにリクエストを送る
        $response = $next($request);

        return $response;
    }
}

 

$response = $next($request);

ここで$next()クロージャを利用してコントローラにリクエストを送っています。

それからエラーハンドリングを行うので非同期的な実装になっています。

 

app/Http/Middleware/CheckAccessToken.php

<?php

namespace App\Http\Middleware;

use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;

class CheckAccessToken
{
    /**
     * リクエストのヘッダーのトークンを検証
     *
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $string = $token = JWTAuth::getToken();

        // ヘッダーにAuthorizationが存在するかをチェック
        if ((!$request->header('Authorization'))) {

            return response()->json([
                'error' => Config::get('error.headerAuthorizationMissing')
            ]);
        }

        try {
            // トークンに含まれているユーザは存在するかをチェック
            if (!$user = JWTAuth::parseToken()->authenticate()) {
                return response()->json([
                    'error' => Config::get('error.invalidToken')
                ]);
            }
        } catch (TokenInvalidException $e) {
            // 無効なアクセストークンによるエラー
            return response()->json([
                'error' => Config::get('error.invalidAccessToken')
            ]);
        } catch (TokenExpiredException $e) {
            // アクセストークンの有効期限切れによるエラー
            return response()->json([
                'error' => Config::get('error.expiredAccessToken')
            ]);
        } catch (JWTException $e) {
            // その他の原因によるトークンのエラー
            return response()->json([
                'error' => Config::get('error.tokenSomethingWentWrongError')
            ]);
        }
        // コントローラにリクエストを送る
        $response = $next($request);

        return $response;
    }
}

 

 

 

 

 

コンフィグの設定

エラーメッセージのレスポンスデータを定義します。

 

config/error.php

<?php

return [
    'failedValidationError' => [
        'code' => 400001,
        'message' => 'リクエストの形式が異なります。'
    ],
    'tokenSomethingWentWrongError' => [
        'code' => 401000,
        'message' => 'トークンのその他のエラーです。'
    ],
    'headerAuthorizationMissing' => [
        'code' => 401001,
        'message' => 'authorizationヘッダが無い/空です。'
    ],
    'invalidAccessToken' => [
        'code' => 401002,
        'message' => 'トークンが無効です。'
    ],
    'expiredAccessToken' => [
        'code' => 401003,
        'message' => 'トークンが有効期限切れです。'
    ],
    'tokenValidDate' => [
        'code' => 401004,
        'message' => 'トークンの発行日時がパスワード最終更新日時より前です。'
    ],
    'wrongIdOrPassword' => [
        'code' => 401005,
        'message' => 'ログインidまたはパスワードが間違っています。'
    ],
    'lockAccount' => [
        'code' => 401006,
        'message' => 'アカウントがロックされています。'
    ],
    'refreshTokenValidDate' => [
        'code' => 401007,
        'message' => 'リフレッシュトークンが無効です。'
    ],
    'refreshTokenExpireDate' => [
        'code' => 401008,
        'message' => 'リフレッシュトークンが有効期限切れです。'
    ],

・・・

 

コンフィグの反映

$ php artisan config:clear
$ php artisan config:cache

 

ミドルウェアの登録

routes/api.phpで利用できるようにKernel.phpに定義します。

 

app/Http/Kernel.php

・・・

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
+       'jwt_auth'  =>  \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
+       'jwt_refresh' => \App\Http\Middleware\CheckRefreshtoken::class,
+       'jwt_access' => \App\Http\Middleware\CheckAccessToken::class,
    ];

    /**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
+       \App\Http\Middleware\CheckRefreshtoken::class,
+       \App\Http\Middleware\CheckAccessToken::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\Authenticate::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

 

ルーティング設定

api/V1_0/refresh-tokenを定義します。

 

routes/api.php

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/


Route::prefix('v1_0')->group(function () {

    Route::middleware([
        'jwt_refresh', // JWTリフレッシュトークン検証 - \App\Http\Middleware\CheckRefreshToken::class
    ])->group(function () {
        Route::post('/refresh-token', 'Api\V1_0\RefreshTokenController@refreshToken');
    });


    Route::group([
        "middleware" => 'guest:api', // 認証不要なAPIとして設定
    ], function () {
        Route::post('/users/me', 'Api\V1_0\RegisterController@register');
        Route::post('/login', 'Api\V1_0\LoginController@login');
        Route::post('/login/guest', 'Api\V1_0\GuestLoginController@login');
    });
});

 

 

@see

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

Laravel6 jwt-auth トークンエラーハンドリング Middleware”への4件のコメント

  1. Laravel:6
    tymon/jwt-auth: 1.0.0-rc.5

    WebAPIにてJWTを実装しています。
    ミドルウェアにて、トークンのチェックをしたく、参考にさせていただきました。
    ありがとうございます。
    トークンの発行やトークンのチェックは上手く動き、
    問題なさそうなのですが、VSCodeデバッグ時にログイン処理を行うと、必ず下記例外が発生します。
    Tymon\JWTAuth\Exceptions\JWTException: The token could not be parsed from the request

    ログイン処理時は、認証チェックは動かないはずなのですが、
    なぜでしょうか?

    ルーティングは下記のようにしています。

    // ゲスト
    Route::group([‘guest’,’api’],function ()
    {
    // ログイン
    Route::post(‘login’, ‘Auth\APILoginController@login’);
    });

    // JWT認証チェック
    Route::middleware([‘api’,’jwt_access’])->group(function ()
    {
    // ユーザー登録
    Route::post(‘register’, ‘Auth\APILoginController@register’);

    // ユーザー変更

    // ユーザー削除

    // ログアウト
    Route::post(‘logout’, ‘Auth\APILoginController@logout’);

    // リフレッシュ
    Route::post(‘refresh’, ‘Auth\APILoginController@refresh’);

    // ログインユーザー情報
    Route::post(‘me’, ‘Auth\APILoginController@me’);
    });

  2. 参考になられたみたいで良かったです☺️

    下記かな?
    https://stackoverflow.com/questions/39218757/laravel-jwt-auth-the-token-could-not-be-parsed-from-the-request

    ご提示された解像度ではこれ以上は難しいです、
    ごめんなさい😭
    少し先ですが1月25日に秋葉原で勉強会やるのでご都合よろしければ、
    懇親会でご状況を確認することはできます🐱🌟
    https://8989work.connpass.com/event/161585/

    1. 返信ありがとうございます。

      結論から言うと、VSCodeのデバック時に、左側のブレークポインにて、
      EverythingをOFFにすると、例外で止まる減少は起きなくなりました。
      ただ、ライブラリにて例外が発生するのは、まぁ気になりますが、
      実害なさそうなので、これで良しとします。
      ありがとうございました:D

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)