Edited at

Laravel5.5こと始め 〜10. APIへのJWTAuth認証の追加〜

More than 1 year has passed since last update.


内容

以下の順番にまとめます。

1. MacへのXAMPP+Laravelインストール

2. ユーザログイン機能の追加

3. MVCとルーティングの説明

4. ユーザリストの表示

5. ユーザリストのペジネーション表示

6. ユーザ管理APIの追加

7. Vue.jsとAPIベースのユーザ管理アプリの追加準備

8. Vue.jsとAPIベースのユーザ管理アプリの追加

9. Vue.jsとAPIベースのユーザ管理アプリへのペジネーション追加

10. APIへのJWTAuth認証の追加 ←いまここ

11. Vue.jsとAPIベースのユーザ管理アプリへの認証の追加


10. APIへのJWTAuth認証の追加

home2へのアクセス時にログインを求められますが、APIには認証の実装がなされておらずAPIアクセスは誰でもできてしまいますので、このままでは問題です。そのため、home2へのアクセスは誰でもできるようにし、APIアクセスにJWTAuthを使って認証の機能を実装します。その後、次ページでVue.jsで実装されたSPA画面上でログインを実装することとします。


10.1 URI「home2」の認証を削除

認証はルーティング部分で設定していますので、「routes/web.php」を編集し、home2に認証なしでアクセスできるようにします。


routes/web.php

...()

//Route::middleware('auth')->get('/home2', function () {
// return view('home2');
//})->name('home2');

Route::get('/home2', function () {
return view('home2');
});


「php artisan serve」を実行して、「http://localhost:8000/home2 」にアクセスし、ログアウトしてもアクセスできることを確認します。

スクリーンショット 2018-07-16 23.20.58.png


10.2 APIへのJWTAuth認証の追加


1. JWTAuthのインストール

Composerコマンドを実行してJWTAuthをインストールします。

$ composer require tymon/jwt-auth

Using version ^0.5.12 for tymon/jwt-auth
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 4 installs, 0 updates, 0 removals
- Installing symfony/polyfill-util (v1.8.0): Loading from cache
- Installing symfony/polyfill-php56 (v1.8.0): Loading from cache
- Installing namshi/jose (7.2.3): Loading from cache
- Installing tymon/jwt-auth (0.5.12): Loading from cache
namshi/jose suggests installing phpseclib/phpseclib (Allows to use Phpseclib as crypto engine, use version ^2.0.)
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nunomaduro/collision
Package manifest generated successfully.


2. 「config/app.php」への設定

下記の通り、JWTAuthのprovidersとaliasesを設定します。


config/app.php

... 中略

'providers' => [
... 中略
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
],
... 中略
'aliases' => [
... 中略
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
],
];


3. 「app/Http/Kernel.php」への設定

下記の通り、ルーティングでJWTAuthが使えるように設定します。


app/Http/Kernel.php

... 中略

protected $routeMiddleware = [
'auth' => \Illuminate\Auth\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,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class, // 追加
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class, // 追加
];
}



4. JWTAuthの設定ファイル作成

以下のコマンドを実行して「config/jwt.php」ファイルを作成します。

$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"

Copied File [/vendor/tymon/jwt-auth/src/config/config.php] To [/config/jwt.php]
Publishing complete.


5. 秘密鍵の作成

以下のコマンドを実行して秘密鍵の生成を試みます。

$ php artisan jwt:generate

ReflectionException : Method Tymon\JWTAuth\Commands\JWTGenerateCommand::handle() does not exist

at /Users/Retina/userauth/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:135
131| $callback = explode('::', $callback);
132| }
133|
134| return is_array($callback)
> 135| ? new ReflectionMethod($callback[0], $callback[1])
136| : new ReflectionFunction($callback);
137| }
138|
139| /**

Exception trace:

1 ReflectionMethod::__construct(Object(Tymon\JWTAuth\Commands\JWTGenerateCommand), "handle")
/Users/Retina/userauth/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:135

2 Illuminate\Container\BoundMethod::getCallReflector()
/Users/Retina/userauth/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:115

Please use the argument -v to see more details.

「JWTGenerateCommand.php」にhandle関数がないためエラーが発生しますので、以下の通りhandle関数を追加します。


vendor/tymon/jwt-auth/src/Commands/JWTGenerateCommand.php

... 中略

public function handle() {
$this->fire();
}
}

再度秘密鍵の生成を試みます。

$ php artisan jwt:generate

jwt-auth secret [thQFW5fwIfpQiHSKkZfjblb0A9TP8dUP] set successfully

「config/jwt.php」ファイルに秘密鍵の設定が追加されていることを確認します。

$ grep secret config/jwt.php 

'secret' => env('JWT_SECRET', 'thQFW5fwIfpQiHSKkZfjblb0A9TP8dUP'),


6. 認証関連のAPIの実装

ここまでに「UserController.php」で定義されたAPIはルーティング「routes/api.php」での定義も含め、全く認証の設定がなされていませんでした。そのため、以下の3点の設定を行います。

1) 認証キー発行のAPI実装(これは認証なしでアクセス可能)

2) ログインしているユーザの情報を返すAPI実装(要認証)

3) 既存ユーザー情報管理APIは認証がないとアクセスができないよう設定

これらは、「UserController.php」へのAPI実装と「routes/api.php」へのルーティング設定で行います。


1) 「UserController.php」へのAPI実装


app/Http/Controllers/UserController.php

<?php

namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;

class UserController extends Controller
{
... 中略
public function authenticate(Request $request) // emailとpasswordでアクセストークンを取得して返します
{
$credentials = $request->only('email', 'password');
try {
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
$user = User::where('email', $request->email)->first();
return response()->json(compact('user', 'token'));
}

public function getCurrentUser() // トークンに紐づくユーザ情報を取得して返します
{
$user = JWTAuth::parseToken()->authenticate();
return response()->json(compact('user'));
}
}


これにより、「1) 認証キー発行のAPI実装(これは認証なしでアクセス可能)」と「2) ログインしているユーザの情報を返すAPI実装(認証なし)」が実装されます。次にルーティングと認証の設定を追加します。


2) 「routes/api.php」へのルーティング設定


routes/api.php

... 中略

Route::post('authenticate','UserController@authenticate');
Route::group(['middleware' => 'jwt.auth'], function () {
Route::resource('users', 'UserController');
Route::get('me', 'UserController@getCurrentUser');
});

これにより、「認証キー発行のAPI」と「既存ユーザー情報管理API」に認証が必要となり、ルーティングの設定が完了します。


7. JWTAuth認証のテスト

例のごとく、「php artisan serve」を実行して、APIのテストを行います。


1) アクセストークンの取得

$ curl -X POST localhost:8000/api/authenticate -d 'email=test1@test.com' -d 'password=(uhBnji9' | jq

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 433 0 395 100 38 2392 230 --:--:-- --:--:-- --:--:-- 2393
{
"user": {
"id": 1,
"name": "test1",
"email": "test1@test.com",
"created_at": "2018-07-18 21:41:12",
"updated_at": "2018-07-18 21:41:12"
},
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC9hcGkvYXV0aGVudGljYXRlIiwiaWF0IjoxNTMxOTg4ODg5LCJleHAiOjE1MzE5OTI0ODksIm5iZiI6MTUzMTk4ODg4OSwianRpIjoiYm5FQlBXNVBSanBEZ0FrYSJ9.Q9ytQ-sLFsg92d5GcPytIKlFJuVA55vQBaAo1LqayNo"
}

ここでいう、"token"の値がアクセストークンになります。


2) APIの実行

アクセストークンなしでAPIを実行すると「token_not_provided」と言われエラーになります。

$ curl -X GET localhost:8000/api/users | jq

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 30 0 30 0 0 413 0 --:--:-- --:--:-- --:--:-- 416
{
"error": "token_not_provided"
}

取得したアクセストークンをセットしてAPIを実行するとうまくいきます。

以下、ユーザー情報取得するAPIの実行です。

$ curl -X GET localhost:8000/api/users  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC9hcGkvYXV0aGVudGljYXRlIiwiaWF0IjoxNTMxOTg4ODg5LCJleHAiOjE1MzE5OTI0ODksIm5iZiI6MTUzMTk4ODg4OSwianRpIjoiYm5FQlBXNVBSanBEZ0FrYSJ9.Q9ytQ-sLFsg92d5GcPytIKlFJuVA55vQBaAo1LqayNo'| jq

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 449 0 449 0 0 1915 0 --:--:-- --:--:-- --:--:-- 1910
{
"current_page": 1,
"data": [
{
"id": 1,
"name": "test1",
"email": "test1@test.com",
"created_at": "2018-07-18 21:41:12",
"updated_at": "2018-07-18 21:41:12"
}
],
"first_page_url": "http://localhost:8000/api/users?page=1",
"from": 1,
"last_page": 4,
"last_page_url": "http://localhost:8000/api/users?page=4",
"next_page_url": "http://localhost:8000/api/users?page=2",
"path": "http://localhost:8000/api/users",
"per_page": 1,
"prev_page_url": null,
"to": 1,
"total": 4
}

以下、アクセストークンに対応するユーザ情報を取得するAPIの実行です。

$ curl -X GET localhost:8000/api/me  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC9hcGkvYXV0aGVudGljYXRlIiwiaWF0IjoxNTMxOTg4ODg5LCJleHAiOjE1MzE5OTI0ODksIm5iZiI6MTUzMTk4ODg4OSwianRpIjoiYm5FQlBXNVBSanBEZ0FrYSJ9.Q9ytQ-sLFsg92d5GcPytIKlFJuVA55vQBaAo1LqayNo'| jq

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 127 0 127 0 0 440 0 --:--:-- --:--:-- --:--:-- 440
{
"user": {
"id": 1,
"name": "test1",
"email": "test1@test.com",
"created_at": "2018-07-18 21:41:12",
"updated_at": "2018-07-18 21:41:12"
}
}

以上、「10. APIへのJWTAuth認証の追加」の完了です。

次は、「11. Vue.jsとAPIベースのユーザ管理アプリへの認証の追加」です。


参考URL

参考1. JWTAuthのインストール方法 http://jwt-auth.readthedocs.io/en/develop/laravel-installation/#add-service-provider-laravel-54-or-below

参考2. 秘密鍵生成「php artisan jwt:generate」が失敗しないようにするためのJWTGenerateCommand.phpの編集 https://laracasts.com/discuss/channels/laravel/class-tymonjwtauthprovidersjwtauthserviceprovider-not-found-1