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
- https://github.com/tymondesigns/jwt-auth/issues/872
- https://blog.pusher.com/laravel-jwt/
- https://qiita.com/yh1224/items/bd00e85d5c53350e93ca
- https://dev-yakuza.github.io/laravel/jwt-user-info/
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’);
});
参考になられたみたいで良かったです☺️
下記かな?
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/
返信ありがとうございます。
結論から言うと、VSCodeのデバック時に、左側のブレークポインにて、
EverythingをOFFにすると、例外で止まる減少は起きなくなりました。
ただ、ライブラリにて例外が発生するのは、まぁ気になりますが、
実害なさそうなので、これで良しとします。
ありがとうございました:D
☺️