Help us understand the problem. What is going on with this article?

FOSUserBundle: Symfonyでユーザ管理

More than 3 years have passed since last update.

はじめに

The FOSUserBundle, developed by the Symfony community, adds support for a database-backed user system in Symfony. It also handles common tasks like user registration and forgotten password functionality.

FOSUserBundleはSymfonyコミュニティにより開発されており
データベースを使用したユーザ管理システムを提供します。
ユーザ登録やパスワードのリセットなどのよくある機能が実装されています。

Symfonyのインストール時にデフォルトでは入りませんが
ドキュメントがSymfony.com以下にあったり
上記のような記載がベストプラクティスに載っていたりと、信頼できそうです。

基本的な機能や気になったところをまとめます。

:warning: セキュリティに関わることなので、内容を鵜呑みにはしないでください。

環境

Symfonyが動作し、Doctrine経由でMySQLにアクセスできることが前提です。

ソフト バージョン
PHP 7系
Symfony 3系
MySQL 5.6.x
Doctrine 2.5.3

(バージョン情報の記憶が曖昧なため、後日追記)

導入

インストール

ドキュメントのInstallationに従います。

作成されるページ

ルーティング

自動で多くのページが生成されます。
機能的にはURLパス見たまんまなので詳細は割愛します。

$ bin/console debug:router | grep fos                               
  fos_user_security_login             GET|POST   ANY      ANY    /login                   
  fos_user_security_check             POST       ANY      ANY    /login_check             
  fos_user_security_logout            GET|POST   ANY      ANY    /logout                  
  fos_user_profile_show               GET        ANY      ANY    /profile/                
  fos_user_profile_edit               GET|POST   ANY      ANY    /profile/edit            
  fos_user_registration_register      GET|POST   ANY      ANY    /register/               
  fos_user_registration_check_email   GET        ANY      ANY    /register/check-email    
  fos_user_registration_confirm       GET        ANY      ANY    /register/confirm/{token}
  fos_user_registration_confirmed     GET        ANY      ANY    /register/confirmed      
  fos_user_resetting_request          GET        ANY      ANY    /resetting/request       
  fos_user_resetting_send_email       POST       ANY      ANY    /resetting/send-email    
  fos_user_resetting_check_email      GET        ANY      ANY    /resetting/check-email   
  fos_user_resetting_reset            GET|POST   ANY      ANY    /resetting/reset/{token} 
  fos_user_change_password            GET|POST   ANY      ANY    /profile/change-password

ログイン

1.png

登録

2.png

パスワードリセット

3.png

見た目が簡素ですが、もちろんテンプレートの書き換えはできます。
Overriding Default FOSUserBundle Templates

作成されるテーブル

DBには以下のテーブルが生成されます。

mysql> DESC user;
+-----------------------+--------------+------+-----+---------+----------------+
| Field                 | Type         | Null | Key | Default | Extra          |
+-----------------------+--------------+------+-----+---------+----------------+
| id                    | int(11)      | NO   | PRI | NULL    | auto_increment |
| username              | varchar(180) | NO   |     | NULL    |                |
| username_canonical    | varchar(180) | NO   | UNI | NULL    |                |
| email                 | varchar(180) | NO   |     | NULL    |                |
| email_canonical       | varchar(180) | NO   | UNI | NULL    |                |
| enabled               | tinyint(1)   | NO   |     | NULL    |                |
| salt                  | varchar(255) | NO   |     | NULL    |                |
| password              | varchar(255) | NO   |     | NULL    |                |
| last_login            | datetime     | YES  |     | NULL    |                |
| locked                | tinyint(1)   | NO   |     | NULL    |                |
| expired               | tinyint(1)   | NO   |     | NULL    |                |
| expires_at            | datetime     | YES  |     | NULL    |                |
| confirmation_token    | varchar(255) | YES  | UNI | NULL    |                |
| password_requested_at | datetime     | YES  |     | NULL    |                |
| roles                 | longtext     | NO   |     | NULL    |                |
| credentials_expired   | tinyint(1)   | NO   |     | NULL    |                |
| credentials_expire_at | datetime     | YES  |     | NULL    |                |
+-----------------------+--------------+------+-----+---------+----------------+
18 rows in set (0.00 sec)

フィールドごとの役割は以下のとおりです。
ドキュメントに記載が見当たらないので、ソースコードからザッと読んだ内容です。

フィールド 役割
id ユニークID
username ユーザ名
username_canonical usernameをすべて小文字に変換したもの。ユーザ名の重複防止に使用
email メールアドレス
email_canonical emailをすべて小文字に変換したもの。メールアドレスの重複防止に使用
enabled ログイン可能かを示す。未アクティベートユーザのログイン禁止などに使用
salt パスワードのハッシュ化に使用されるソルト文字列
password 暗号化されたパスワード
last_login 最終ログインの日時
locked ログイン可能かを示すフラグ。強制的なログイン禁止などに使用
expired アカウントの有効期限切れを示す。期限が切れているとログイン不可
expires_at アカウントの有効期限
confirmation_token パスワードリセットにおける本人確認などに使用されるトークン
password_requested_at ユーザがパスワードリセットをリクエストした日時
roles アクセスコントロールに使用されるロール
credentials_expired 認証情報の有効期限切れを示す。定期的なパスワード変更を促すのに使用
credentials_expire_at 認証情報の有効期限

気になったこと

userテーブルに他のフィールドを追加できないのか

必要最低限のフィールドは自動的に作成されましたが
アプリケーションの要件によっては他のフィールドが必要です。

この場合にはエンティティに追加フィールド情報をマッピングできるようです。
例えばアイコン表示用に画像パスを格納したい場合は以下のように記載します。

User.php
/**
 * @var string
 *
 * @ORM\Column(name="image_path", type="string", length=255)
 */
protected $image_path;

その後にスキーマを再更新します。

スキーマの更新
$ bin/console doctrine:schema:update --force

新しくフィールドが追加されているのがわかります。

mysql> DESC user;
+-----------------------+--------------+------+-----+---------+----------------+
| Field                 | Type         | Null | Key | Default | Extra          |
+-----------------------+--------------+------+-----+---------+----------------+
| id                    | int(11)      | NO   | PRI | NULL    | auto_increment |
| username              | varchar(180) | NO   |     | NULL    |                |
| username_canonical    | varchar(180) | NO   | UNI | NULL    |                |
| email                 | varchar(180) | NO   |     | NULL    |                |
| email_canonical       | varchar(180) | NO   | UNI | NULL    |                |
| enabled               | tinyint(1)   | NO   |     | NULL    |                |
| salt                  | varchar(255) | NO   |     | NULL    |                |
| password              | varchar(255) | NO   |     | NULL    |                |
| last_login            | datetime     | YES  |     | NULL    |                |
| locked                | tinyint(1)   | NO   |     | NULL    |                |
| expired               | tinyint(1)   | NO   |     | NULL    |                |
| expires_at            | datetime     | YES  |     | NULL    |                |
| confirmation_token    | varchar(255) | YES  | UNI | NULL    |                |
| password_requested_at | datetime     | YES  |     | NULL    |                |
| roles                 | longtext     | NO   |     | NULL    |                |
| credentials_expired   | tinyint(1)   | NO   |     | NULL    |                |
| credentials_expire_at | datetime     | YES  |     | NULL    |                |
| image_path            | varchar(255) | NO   |     | NULL    |                |
+-----------------------+--------------+------+-----+---------+----------------+
18 rows in set (0.00 sec)

Userエンティティの基本的な作りは他と変わらないので
リレーションを定義することも同様に可能です。

パスワードのポリシー(長さや使用文字制限)は設定できないのか

内部では、Entity、Form、FormTypeなどが使われているので
それぞれにバリデーションを加えることで実現できそうです。
Overriding Default FOSUserBundle Forms

デフォルトの設定はResources/config/validation.xmlにあるから
使うなら使えばいいし、だめならオーバーライドというスタンスなんでしょうか。
Overriding Default FOSUserBundle Validation

ユーザ名を使わないとログインできないのか

installationでの手順ではユーザ名しか使用できませんでしたが
メールアドレスでのログインも許可できるようです。

security.ymlidの部分を変更します。

security.yml
providers:                                       
    fos_userbundle:                              
        id: fos_user.user_provider.username_email

これで、ユーザ名とメールアドレスのどちらかでのログインが可能です。
メールアドレスのみを許すことはできませんが、拡張についてPRがありました。
Added email only authentication

まとめ

デフォルトではセキュリティが割と緩めなので
詳細な設定についてはドキュメントをご一読ください。
全体的に「気になるところはオーバーライドして」というスタンスな気がします。

Webのセキュリティ知識や内部実装への理解は必要ですが
上記のような簡単な手順だけで多くの機能を得られるのはお得ですね。

nanakenashi
都内に勤務のエンジニア。
lifull
日本最大級の不動産・住宅情報サイト「LIFULL HOME'S」を始め、人々の生活に寄り添う様々な情報サービス事業を展開しています。
https://lifull.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした