Symfonyユーザー認証機能の実装について簡単に紹介！

[Symfony Advent Calendar 2023](https://qiita.com/advent-calendar/2023/symfony) の13日目の記事です🎄✨

今回の記事では「Syfmonyでユーザー認証機能の実装」について紹介します！


## この記事のゴール

ユーザー認証の機能実装ができるというところを目指します！

## 目次

1. [環境](#1-環境)
2. [作成済みリポジトリ](#2-作成済みリポジトリ)
3. [ユーザー認証機能の実装について](#3-ユーザー認証機能の実装について)
    1. [プロジェクトの作成/設定](#3-1-プロジェクトの作成設定)
    2. [必要なbundleのインストール](#3-2-必要なbundleのインストール)
    3. [Userクラスの新規作成](#3-3-userクラスの新規作成)
    4. [ログイン機能を作成](#3-4-ログイン機能を作成)
    5. [ユーザーを作成するコマンドを作成](#3-5-ユーザーを作成するコマンドを作成)
    6. [パスワードのハッシュ化とログイン後の遷移先を変更](#3-6-パスワードのハッシュ化とログイン後の遷移先を変更)
 4. [実際に試してみる](#4-実際に試してみる)

## 1. 環境

- PHP：`v8.2.12`
- Symfony：`v7.0.1`
- MySQL：`v8.0.33`


## 2. 作成済みリポジトリ

https://github.com/mako5656/202312-symfony-user-certification

## 3. ユーザー認証機能の実装について

### 3-1. プロジェクトの作成/設定

- 新規プロジェクト作成

```
symfony new [プロジェクト名]
```

`symfony server:start`して`https://127.0.0.1:8000`を開いたときに下記画面であれば無事作成できてます！

![スクリーンショット 2023-12-09 15.22.22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552607/ea51f431-03b0-b8e0-6035-d5038311556a.jpeg)


### 3-2. 必要なbundleのインストール

```
composer require orm
composer require symfony/maker-bundle --dev
composer require security
composer require twig
```

### 3-3. Userクラスの新規作成

Symfony MakerBundleを使用して、`UserEntity`を作成します。

```
bin/console make:user
```

<details>

<summary>make:entityを使ったUserEntityクラスのコマンド詳細</summary>

```
$ bin/console make:user

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

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

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

 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]:
 > 

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

           
  Success! 
           

 Next Steps:
   - Review your new App\Entity\User class.
   - Use make:entity to add more fields to your User entity and then run make:migration.
   - Create a way to authenticate! See https://symfony.com/doc/current/security.html

```

</details>

詳しくは、[「Symfony公式ドキュメントのSecurityのThe User」](https://symfony.com/doc/current/security.html#the-user)というところに詳しく書いてありますが、行っていることは下記です。

1. `UserEntity`:`src/Entity`ディレクトリ内にUserEntityが生成されます。ユーザーに関する情報をデータベースに保存するためのEntityです。
2. `UserRepository`: `src/Repository`ディレクトリ内にUserEntityに対するRepositoryクラスが生成されます。リポジトリはデータベースとのやりとりを管理します。
3. `セキュリティ関連の設定の更新`:`config/packages/security.yaml`などの設定ファイルが更新され、ユーザー認証に関する設定が追加されます。

[コードの変更点はこちら](https://github.com/mako5656/202312-symfony-user-certification/commit/03a9d6da1312d3171f1d7daa08a40d4db9fac8d1#diff-19446c4b69407952b20ae26dbd032cdad8dcc487db081a5cb17261831e80a4cc)

:::note warn
最初`make:entity`コマンドでUserEntityクラスを新規作成したんですがUserクラスに`UserInterface`が必要だったりしたのでこちらの方が楽です。
:::

その後データベースをマイグレーションすれば、`User`クラスの作成は完了です！

```
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
```


### 3-4. ログイン機能を作成

下記コマンドでログイン画面とログイン処理を作成できます。

```
bin/console make:auth
```

<details>

<summary>make:authを使ったログイン機能の作成コマンド詳細</summary>

```
$ bin/console make:auth

 What style of authentication do you want? [Empty authenticator]:
  [0] Empty authenticator
  [1] Login form authenticator
 > 1

 The class name of the authenticator to create (e.g. AppCustomAuthenticator):
 > LoginFormAuthenticator

 Choose a name for the controller class (e.g. SecurityController) [SecurityController]:
 > UserController

 Do you want to generate a '/logout' URL? (yes/no) [yes]:
 >

 Do you want to support remember me? (yes/no) [yes]:
 >

 How should remember me be activated? [Activate when the user checks a box]:
  [0] Activate when the user checks a box
  [1] Always activate remember me
 >

 created: src/Security/LoginFormAuthenticator.php
 updated: config/packages/security.yaml
 created: src/Controller/UserController.php
 created: templates/security/login.html.twig

  Success!

 Next:
 - Customize your new authenticator.
 - Finish the redirect "TODO" in the App\Security\LoginFormAuthenticator::onAuthenticationSuccess() method.
 - Review & adapt the login template: templates/security/login.html.twig.
```

</details>

1. `src/Security/LoginFormAuthenticator.php`: ログインフォームの認証を担当するAuthenticatorクラス。このクラスはSymfonyセキュリティコンポーネントによって使用され、ユーザーがログインフォームで提供した認証情報を処理します。
2. `config/packages/security.yaml`: 新しく生成されたAuthenticatorがセキュリティの設定に追加された。
3. `src/Controller/UserController.php`: ユーザーに関連するアクションやビューを処理する機能。
4. `templates/security/login.html.twig`: ユーザーにログインフォームを表示するためのもの。

[コードの変更点はこちら](https://github.com/mako5656/202312-symfony-user-certification/commit/b2d8ace176edc14465cf425672086ca42285b7ff)

詳しくは、[「Symfony公式ドキュメントのSecurityのForm Login」](https://symfony.com/doc/current/security.html#form-loginer)というところに詳しく書いてあります。

### 3-5. ユーザーを作成するコマンドを作成

ログインできるユーザーをコマンドで作成できるように`UserCreateCommand`を作成します。

```
bin/console make:command
```
を実行しテンプレートを作成し[このように中身を変更](https://github.com/mako5656/202312-symfony-user-certification/blob/main/src/Command/UserCreateCommand.php)します。

これで下記コマンドで「メールアドレス」「パスワード」「権限」をつけてデータベースに保存できます。

```
bin/console app:user:create email@test.com password -r ROLE_ALLOWED_TO_ADMIN
```

データベースを見て先程入れた内容が入っていれば問題ないです！

![スクリーンショット 2023-12-11 13.09.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552607/62686094-ce43-227c-4f45-53f097430f4c.png)

※Symfonyでのデータベースとの接続については[このあたりのセットアップ](https://tech.quartetcom.co.jp/2023/06/29/memo-app-symfony6/#21-%E4%BB%8A%E3%81%8B%E3%82%89%E4%BD%9C%E3%82%8B%E3%83%A1%E3%83%A2%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99%E3%81%9F%E3%82%81%E3%81%AE%E6%BA%96%E5%82%99)を確認ください

### 3-6. パスワードのハッシュ化とログイン後の遷移先を変更

- パスワードのハッシュ化しないように変更

※今回は簡単な実装をメインにしているので実際のプロジェクトではハッシュ化やその他の暗号化を行ってください

```diff:config/packages/security.yaml
 security:
     # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
     password_hashers:
-        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
+        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
+            algorithm: plaintext
     # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
     providers:
         # used to reload user from session & other features (e.g. switch_user)
(
```

- ログイン後にログインフォーム画面に遷移するように変更

```diff:src/Security/LoginFormAuthenticator.php
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
 
         // For example:
         // return new RedirectResponse($this->urlGenerator->generate('some_route'));
-        throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
+        return new RedirectResponse($this->urlGenerator->generate(self::LOGIN_ROUTE));
     }
 
     protected function getLoginUrl(Request $request): string
(

```

これで準備完了です！！！！！！！

## 4. 実際に試してみる

`https://127.0.0.1:8000/login`を開くと下記画面になるので、上記で入れた「メールアドレス」「パスワード」を入れてログインでき、`https://127.0.0.1:8000/logout`でログアウトが無事できたら成功です🎉🎉🎉

ログイン後にページをリロードしてもログアウトしない限りずっとログイン状態が続いていると思います✨

![スクリーンショット 2023-12-09 21.45.32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552607/bb1fbf3b-d26d-78b0-700f-1b96f96d3939.jpeg)

![スクリーンショット 2023-12-11 11.54.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552607/0cf44046-d969-c675-a341-423231e774da.png)

## 参考

- Zenn：ユーザー認証周りの機能を一通り実装

https://zenn.dev/ttskch/books/2fa3612c68c2d9/viewer/3