LoginSignup
0
0

More than 1 year has passed since last update.

Symfony Security と JWT の設定方法

Posted at

lexik/jwt-authentication-bundle を利用して API のセキュリティを JWT で構成します。

  • Symfony のバージョンは 5.3 を使っています。(5.2 以前とは設定が一部違うようです)
  • MakerBundle が必要です。

jwt-authentication-bundle の導入

Getting started の手順に従って導入を進めます。

composer.phar require lexik/jwt-authentication-bundle

上記を実行すると依存関係を解決して、Symfony Security Bundle も導入されます。

flex のおかげで、bundles.php への追加や、各種設定ファイルの更新なども行ってくれます。

SSL キーの生成

次のコマンドを実行します。

php bin/console lexik:jwt:generate-keypair

.env ファイルに次の行が追加されます

###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=****************
###< lexik/jwt-authentication-bundle ###

config/packages/lexik_jwt_authentication.yaml が作成されます。

lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'

これらの設定ファイルが更新された状態から、後ほど設定を変更して行きます。

ユーザー情報

jwt-authentication-bundle の設定を進める前に、ユーザー情報の作成をしておきます。

ここでは一般的な、 データベースに保存されたユーザー情報を使う Entity プロバイダーを設定します。
ログインユーザー情報を作成します。

ユーザー Entity の作成

プロジェクトのルートで次のコマンドを実行し、ユーザー Entity を作成します。

php bin/console make:user

実行するとプロンプトが表示されますので、質問に答えます。

The name of the security user class (e.g. User) [User]:
 > User

 Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
 > yes

 Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
 > username

 Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).

 Does this app need to hash/check user passwords? (yes/no) [yes]:
 > yes

 created: src/Entity/User.php
 created: src/Repository/UserRepository.php
 updated: src/Entity/User.php
 updated: config/packages/security.yaml

User エンティティとリポジトリが作成されます。
同時に、security.yaml も更新されます。

テーブルの作成

bin/console doctrine:schema:update --force

もちろん migration を使って作成しても構いません。

ユーザー情報の登録

ユーザー情報のレコードを登録します。

php bin/console make:fixtures UserFixtures

src/DataFixtures/UserFixtures.php が作成されます。
その内容を次のように設定します。

<?php

namespace App\DataFixtures;

use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class UserFixtures extends Fixture
{
    public function __construct(
        private UserPasswordHasherInterface $passwordHasher
    ) {
    }

    public function load(ObjectManager $manager): void
    {
        $user = new User();
        $user
            ->setUsername('admin')
            ->setPassword($this->passwordHasher->hashPassword($user, 'password'))
            ->setRoles(['ROLL_ADMIN', 'ROLL_USER']);
        $manager->persist($user);
        $manager->flush();
    }
}
  • パスワードをハッシュするために constructor に UserPasswordHasherInterface を渡すようにしています。Symfony の DI が注入してくれます
  • ユーザーを1件登録します

データをDBに登録させます。

php bin/console doctrine:fixtures:load
 Careful, database "foo" will be purged. Do you want to continue? (yes/no) [no]:
 > yes

   > purging database
   > loading App\DataFixtures\UserFixtures

以上でユーザー情報の作成は完了です。jwt-authentication-bundle の設定に戻ります。

firewalls の設定

config/packages/security.yaml を編集します

security:
    enable_authenticator_manager: true

    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
        App\Entity\User:
            algorithm: auto

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: username

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            pattern: ^/api/login
            stateless: true
            json_login:
                check_path: /api/login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern:   ^/api
            stateless: true
            jwt: ~

    access_control:
        - { path: ^/api/login, roles: PUBLIC_ACCESS }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

Getting started に従って、firewalls キーと access_control キーを設定しました。

ここまでの設定ができたら、ログイン認証が行えます。

以下のリクエストは JetBrains IDE の http client の形式で記述します。
PHPStorm などで Tools > Http Client > Create Request in Http Client で開いた Http Client に貼り付けて実行することができます。

POST http://localhost:8000/api/login_check
Content-Type: application/json

{"username":"admin","password":"password"}

このリクエストに対して、

{
  "token": "......"
}

とレスポンスが返ってきたらとりあえず設定完了です。あとでここで返された token を使います。

簡単な api を作成して呼び出してみます。

src/Controller/EchoController.php

<?php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class EchoController extends AbstractController
{
    #[Route('/api/echo/{message}',
        name   : 'echo',
        methods: ['GET']
    )]
    public function index(string $message): JsonResponse
    {
        return $this->json([
            'message' => $message
        ]);
    }
}

呼び出します。

http request
GET http://localhost:8000/api/echo/Hello!

次のように Unauthorized エラーが返ります。token を渡していないからです。

{
  "code": 401,
  "message": "JWT Token not found"
}

Authorization ヘッダーにログインの時に返された token を渡します。

http request
GET http://localhost:8000/api/echo/Hello!
Authorization: Bearer *******ThisIsToken***********

次のようにレスポンスが返されるようになります。

{
  "message": "Hello!"
}
0
0
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
0
0