PHP による単純な JWT 認証の例

このページは、PHP で単純な JWT 認証を行う例を記載するページです。

注意

  • コードのライセンスは CC0 (クレジット表示不要、改変可、商用可) です。
  • フレームワークを使わない場合に使う用途として想定しています。
  • このページでは JWT のライブラリとして firebase/php-jwt (6.1.1) を使用しています。
インストール
composer require firebase/php-jwt

サンプルの想定動作

サンプルの想定動作は下記のようなイメージです。

  • login.php に下記のリクエストをすると認証が通り JWT トークン ({ "token": "..." }) が返却される
    • ヘッダー: Content-Type: application/json
    • ボディ: { "username": "test", "password": "test" }
  • data.php に下記のリクエストをするとデータ ({ "username": "test }) が返却される
    • ヘッダー: Content-Type: application/json
    • ヘッダー: Authorization: Bearer 【index.php で取得したトークン】
    • ボディ: なし

コード

.htaccess

RewriteEngine On
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

PHP

const.php
<?php
/** JWT のアルゴリズム */
define('JWT_ALG', 'HS256');
/** JWT のキー */
define('JWT_KEY', '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
/** JWT の発行者 */
define('JWT_ISSUER', 'http://localhost');
/** JWT の有効期限 (秒) */
define('JWT_EXPIRES', 3600);
login.php
<?php
require __DIR__ . '/const.php';
require __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;

// POST 時
if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') {
    $inputString = file_get_contents('php://input'); // JSON 文字列取得
    $input = @json_decode($inputString, true);

    if (is_array($input)) {
        $input = array_merge(array('username' => '', 'password' => ''), $input);
        $username = $input['username'];
        $password = $input['password'];
        
        $ok = ($username == 'test' && $password == 'test'); // username = test, password = test で認証 OK とする (仮)
        if ($ok) {
            $payload = array(
                'iss' => JWT_ISSUER,
                'exp' => time() + JWT_EXPIRES,
                'username' => $username,
            );
            $jwt = JWT::encode($payload, JWT_KEY, JWT_ALG);
    
            header('Content-Type: application/json');
            echo json_encode(array('token' => $jwt)); // token を返却
            return;
        }
    }
    // JSON 取得失敗、認証に失敗した場合は 401
    http_response_code(401);
}
data.php
<?php
require __DIR__ . '/const.php';
require __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// GET 時
if (strtoupper($_SERVER['REQUEST_METHOD']) == 'GET') {
    $auth = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : '';
    if (preg_match('#\ABearer\s+(.+)\z#', $auth, $m)) { // Bearer xxxx...
        $jwt = $m[1];
        try {
            $payload = JWT::decode($jwt, new Key(JWT_KEY, JWT_ALG)); // JWT デコード (失敗時は例外)
            $username = $payload->username; // エンコード時のデータ取得

            header('Content-Type: application/json');
            echo json_encode(array('username' => $username)); // username を返却
            return;
        }
        catch (Exception $e) {}
    }
    // Bearer が取得できない、JWT のでコードに失敗した場合は 401
    http_response_code(401);
}

リクエスト例

※ Visual Studio Code の REST Client 拡張で使用できる形式になっています。data.php の Bearer は login のレスポンスにある token を使用します。

# @name login
POST http://localhost/login.php
Content-Type: application/json

{
    "username": "test",
    "password": "test"
}

###

GET http://localhost/data.php
Content-Type: application/json
Authorization: Bearer {{login.response.body.$.token}}

実行例

login.php 成功時 (失敗時は 401 エラー)
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9sb2NhbGhvc3QiLCJleHAiOjE1ODk1NTI1ODksInVzZXJuYW1lIjoidGVzdCJ9.N7gVU75BNSjxrSPPjBU-bLaUbUuFlMP3YHHq5-vGr5s"
}
data.php 成功時 (失敗時は 401 エラー)
{
  "username": "test"
}