LoginSignup
38
42

More than 3 years have passed since last update.

Laravel + Jwt Auth で認証付きWebAPIを準備する

Last updated at Posted at 2019-07-17

Webアプリケーションをつくるに当たって、最近はフロントエンドをVueやReactなどでごりごり作りSPAとして運用し、サーバーサイドはAPIサーバーとして別実装して、それぞれ独立して運用するということが一般的になりましたね。
また、サーバーサイドから直接ビューを返すより、APIとしておくほうが、モバイルアプリなども運用したり、別アプリと連携させたりと運用に幅を持たせられます。そのため、APIとして運用していきたいのですが、さて、そのときのログイン認証機構をつくるときはどうするのかということをまとめておきたいと思います。
自身のメモとして作成しておりますので、至らない点多々あるかと思います。また、間違っている部分があれば、私のためにご指摘いただけると幸いです。また、内容を推敲した結果、参考にしたサイトからそのまま引用している箇所が多分にありますので、ご了承ください。

一応自分なりに細かくLaravelの挙動やセキュリティの注意点などもまとめているつもりなので初学者の方はきちんと読んでから実装して欲しいです。慣れている方はむしろ間違いを見つけて指摘してもらえたらありがたいです。

説明抜きで端的に実装する手順については最後にまとめてあります。

参考:公式ドキュメント「Laravel5.8認証」
参考:Laravel + Jwt Auth で認証付きWebAPIを作る
参考:Laravelのmake authで認証機能を作る

環境

2019/07/17執筆時点の環境
* PHP7.3
* Laravel5.8

作るもの

  • {ドメイン}/api/login   POSTでユーザーID、パスワードを送信するとトークンが返ってくる
  • {ドメイン}/api/me   Authorizationヘッダーにトークンをセットしてアクセスするとユーザーの情報を返す

1.認証機能を導入

認証機構をつくる上で気をつかうのがCSRFやSQLインジェクションなどのセキュリティ面の対策です。Laravelにはそれらに必須の対策をよしなに準備してくれる機能が用意されているので、さくっと必要なものを準備します。
(セキュリティについては別途きちんと勉強しましょう)

php artisan make:auth ...①
php artisan migrate   ...②

これだけで基本的なログイン機構がつくられています。

①で作られたもの

php artisan make:auth

これによって以下のものが生成されます。

  • レイアウトビュー
    • resources/views/layouts/app.blade.php
  • 登録ログインビュー
    • resources/views/auth/login.blade.php
    • resources/views/auth/register.blade.php
    • resources/views/auth/verify.blade.php
    • resources/views/auth/passwords/email.blade.php
    • resources/views/auth/passwords/reset.blade.php
  • ログイン後のダッシュボード画面
    • HomeController
    • resources/views/home.blade.php - すべての認証エンドポイントのルート
    • routes/api.php(加筆)
    • routes/web.php(加筆)

②で行われること

php artisan migrate

database/migrationsの中のmigrationを全て実行します。
デフォルトでは
create_users_table.php
create_password_resets_table.php
の二つが用意されているので、これらのテーブルがDBに作成されます。

※ここでDBの設定がされていないとエラーが出ます。

WebAPIのときに使うのでユーザーを登録しておきましょう。

2.composerでjwt-authをインストール

jwt-authをLaravelプロジェクトにインストールします。

composer require tymon/jwt-auth 1.0.0-rc.4.1

Laravel5.5>= には1.0以上のjwt-authが必要らしいのでバージョン指定します。
指定なしだと0.5くらいのがインストールされようとして、インストール時にエラーはくので注意。

次にjwt-authの初期設定を行います。

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

config/jwt.phpが生成されます。

JWTで用いる秘密鍵を生成。

php artisan jwt:secret

.envファイルにJWT_SECRETのパラメータが追加されます。

3.Userモデルを修正

Userモデルをjwt-authに対応させるため、編集します。

 <?php

 namespace App;

+use Tymon\JWTAuth\Contracts\JWTSubject;
 use Illuminate\Notifications\Notifiable;
 use Illuminate\Foundation\Auth\User as Authenticatable;

-class User extends Authenticatable
+class User extends Authenticatable implements JWTSubject
 {
    ...

+    public function getJWTIdentifier()
+    {
+        return $this->getKey();
+    }
+    public function getJWTCustomClaims()
+    {
+        return [];
+    }
}

4.guardを修正

guardは認証を管理する仕組みで、デフォルトではwebapiがあります。
webは普通にhtmlからのログインを管理しています。
一方apiは名前の通り、WebAPIのログインです。

jwt-authを使うのでapijwtに変えます。

config/auth.phpを編集

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
-            'driver' => 'token',
+            'driver' => 'jwt',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

5.ApiControllerを生成

次にAPIのコントローラーを作成します。

php artisan make:controller ApiController

app/Http/Controllers/ApiController.phpが生成されます。

以下のように変更しましょう。

<?php

namespace App\Http\Controllers;

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

class ApiController extends Controller
{
    function login() {
        $credentials = request(['email', 'password']);

        if (! $token = auth("api")->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    public function me()
    {
        return response()->json(auth()->user());
    }

    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth("api")->factory()->getTTL() * 60
        ]);
    }
}

ログインとユーザー情報を取得するメソッドを作成しました。

6.routes/api.phpを編集

routes/api.phpファイルを編集。

<?php

use Illuminate\Http\Request;

Route::group(["middleware" => "guest:api"], function () {
    Route::post("/login", "ApiController@login");
});

Route::group(["middleware" => "auth:api"], function () {
    Route::get("/me", "ApiController@me");
});

/loginguestを指定して認証がなくてもアクセスできるように、/meはログインしてトークンを送らないとアクセスできないようにしました。

7.試す

curlで試してみます。(ちなみに私はAPIの検証にはPostmanを使っています)
emailpasswordは上のユーザーを登録したときのを指定します。

curl http://<laravel-host>/api/login -d email=hoge@example.com -d password=hoge12345

すると以下のようにトークンを含んだjsonが返ってきます。

{"access_token":"eyJ0eXAiOiJKV2QiLC2hbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sYXJhdmVsLXRha2FzYW45ODkuYzl1c2Vygy5pb1wvYXBpXC9sb2dpbiIsImlhdCI6MTUyMjE1NTY1OSwiZXhwIjoxNTIyMTU5MjU5xCJuYmYiOjE1MxIxNTU2NTksImp0aSI6Inh6MHRsQ1hHNmgwQ1g3V0UiLCJzdWIiOjEsInBydiI6Ijg3bTBhZjFlZjlmZDE1ODEyZmorYzk3MTUzYTE0ZTBJfDQ3NTQ2YWEifQ.BdoHaKFy8XLOSaTKBOhA1D3i5NPUGzG9E1lsBQefEhs","token_type":"bearer","expires_in":3600}

そしてトークンを使って認証が必要なurlにアクセスしてみます。
AuthorizationヘッダーにBearer: <token>を付けて送信します。

curl -H "Authorization: Bearer eyJ0eXAi...." http://<laravel-host>/api/me

実行すると

{"id":1,"name":"hoge","email":"hoge@example.com","created_at":"2018-03-26 14:33:21","updated_at":"2018-03-26 14:33:21"}

ログインしているユーザーの情報が返ってきました。
これを応用すればユーザー認証ができるWebAPIが作れます。

8.ちょっと補足

ちなみに、ちょっとしたことですが、返ってきたデータはuserレコードまんまでないことはお気づきでしょうか?

返すレコードからpasswordとremenber_tokenのカラムだけ削除して返していることに注目してください。これは実はapp/User.phpファイルの中できちんと隠す項目を設定しているんです。

    protected $hidden = [
        'password', 'remember_token',
    ];

ね?
passwordはもちろん、remenber_tokenは、ログイン情報を記憶しておく際に保存するcookie情報の一部となります。

9.まとめ

ここまでの流れを理解した上でさくっと実装するために完結にまとめます
(ここだけ見てやるのは上を理解したうえで行って欲しいな)

1.さくっと認証機構準備

php artisan make:auth ...①
php artisan migrate   ...②

2.jwt-authのインストールと初期設定

composer require tymon/jwt-auth 1.0.0-rc.4.1
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

3.Userモデルの作成

 <?php

 namespace App;

+use Tymon\JWTAuth\Contracts\JWTSubject;
 use Illuminate\Notifications\Notifiable;
 use Illuminate\Foundation\Auth\User as Authenticatable;

-class User extends Authenticatable
+class User extends Authenticatable implements JWTSubject
 {
    ...

+    public function getJWTIdentifier()
+    {
+        return $this->getKey();
+    }
+    public function getJWTCustomClaims()
+    {
+        return [];
+    }
}

4.guard修正

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
-            'driver' => 'token',
+            'driver' => 'jwt',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

5.APIコントローラ作成

php artisan make:controller ApiController
<?php

namespace App\Http\Controllers;

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

class ApiController extends Controller
{
    function login() {
        $credentials = request(['email', 'password']);

        if (! $token = auth("api")->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    public function me()
    {
        return response()->json(auth()->user());
    }

    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth("api")->factory()->getTTL() * 60
        ]);
    }
}

6.routes編集

<?php

use Illuminate\Http\Request;

Route::group(["middleware" => "guest:api"], function () {
    Route::post("/login", "ApiController@login");
});

Route::group(["middleware" => "auth:api"], function () {
    Route::get("/me", "ApiController@me");
});

7.完了!!

というわけで、認証にjwtを用いて自分の情報を取得してくる流れをまとめました。
最後に確認ですが、JWTはトークンの改変を防ぐ(検知する)仕組みを組み込んだトークンです。改変に強い反面、トークン自体を復号することは容易ですので、JWTトークンの中に重要な情報を含まないように気をつけましょう。

※guardの挙動についてまとめきれなかったけど、後日書き足します。

38
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
42