LoginSignup
2
0

【PHP】マイナーフレームワーク「Flow」を試してみる~認証編~

Posted at

初めに

前回はこちらの記事で、Flowにおけるイベント管理について書きました。
今回はFlowでログイン処理を実装してみましょう。

前提知識

まずは、Flowで認証処理を実装するために抑えておきたい知識を説明します。

どの認証方式を使うか

認証といっても、パスワードやキーやSSOなど、様々な種類があります
Flowでは、それらの認証方式の実現するための2つのインターフェースが用意されています。

1. 認証プロバイダ

認証方式を定義するインターフェースです。

例)パスワード認証、SSOなど

2. トークン

認証ステートを管理するためのインターフェースです。
以下の情報を持ちます。

  • 認証状態
  • どの認証プロバイダを用いているか
  • エントリーポイント(ログアウト後に遷移する画面)

などなど

認証プロバイダとトークンは設定ファイルに記載することで、ログイン処理実行時に参照することが可能です。
※後述します。

認証専用のActionController

Flowでは、認証専用のControllerである AbstractAuthenticationControllerクラスが用意されており、実装する際はこのクラスを継承して実装する必要があります。
AbstractAuthenticationControllerには認証に関する以下のメソッドが定義されています。

1. authenticateAction

認証を行うためのActionです。
認証の際は、クレデンシャルをformに詰めてこのActionを呼ぶ必要があります。

2. onAuthenticationSuccess(※abstract)

ログイン成功時の処理を記載します。
authenticateActionで認証が成功した場合に呼び出されます。

3. onAuthenticationFailure

ログイン失敗時の処理を記載します。
authenticateActionで認証が失敗した場合に呼び出されます。

4. logoutAction

ログアウトするためのActionです。

実際に作成してみる

前提知識がの紹介が終わったので、実際に作ってみましょう。
今回はFlowが提供している認証プロバイダとトークンを用いて、ユーザIDとパスワードを用いた認証を行ってみます。

プロジェクト構成は以下です。

Packages/
  ├ Application/
  |    └ Neos.Welcome/
  |         ├ Classes/
  |         |    └ Controller/
  |         |         ├ AccountController.php
  |         |         ├ LoginController.php
  |         |         └ TopController.php
  |         |
  |         ├ Configration/
  |         |    ├ Policy.yaml
  |         |    └ Settings.yaml
  |         |
  |         └ Resources/
  |              └ Private/
  |                   └ Templates/
  |                        ├ Login/
  |                        |   └ index.html
  |                        └ Top/
  |                            └ index.html
  |
  ├ Framework/
  └ Libraries/

1. アカウントの準備

まずはログインで使用するアカウントを準備します。

アカウントテーブル

Flowのプロジェクトは、マイグレーション実行時にneos_flow_security_accountというテーブルをデフォルトで作成します。PersistedUsernamePasswordProviderは、このテーブルを用いてユーザのログイン情報を管理します。
(なんのテーブルだろうってずっと疑問でした)

カラム名 説明
persistence_object_identifier ユニークID
accountidentifier アカウントID
authenticationprovidername 認証プロバイダ名(★)
credentialssource パスワード(暗号化)
creationdate 作成日時
expirationdate 有効期限
roleidentifiers 権限(★)
lastsuccessfulauthenticationdate 最終ログイン日時
failedauthenticationcount ログイン失敗回数

認証プロバイダ名は、後述するSettings.yamlに定義します。

権限は、Policy.yamlに以下のように定義する必要があります。

Policy.yaml
roles:
  'Neos.Welcome:Everybody':
    label: 'everyone'
    description: 'everyone role'

アカウント登録用Controler

アカウント作成用のAPIを作成します。

neos_flow_security_accountテーブルにINSERTするデータは、accountFactoryaccountRepositoryというFlowのクラスを用いて作成することができます。
認証プロバイダ名を指定しない場合は、Settings.yamlで定義するDefaultProviderが登録されます。

AccountController.php
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;

class AccountController extends ActionController
{
    /**
     * @Flow\Inject
     * @var \Neos\Flow\Security\AccountFactory
     */
    protected $accountFactory;

    /**
     * @var \Neos\Flow\Security\AccountRepository
     * @Flow\Inject
     */
    protected $accountRepository;

    /**
     * @Flow\Inject
     * @var \Neos\Flow\Mvc\View\JsonView
     */
    protected $view;

    /**
     * @param string $username
     * @param string $password
     */
    public function registerAction(String $username, String $password)
    {
        $account = $this->accountFactory->createAccountWithPassword($username, $password, array('Neos.Welcome:Everybody'));
        $this->accountRepository->add($account);
        $this->view->assign('value', array('Account created'));
    }
}

以下のコマンドでアカウントを登録しました。

$ curl -i -X POST \
   -H "Content-Type:application/json" \
   -d \
'{
  "data" {
    "username": "name",
    "password": "pass"
  }
}' \
 'http://localhost:8081/Neos.Welcome/Account/register'

2. ログイン処理の実装

続いてログイン処理を作っていきます。

Controller

ログイン画面及びログイン処理を行うControllerです。
AbstractAuthenticationControllerを継承し、各種メソッドを実装します。

LoginController.php
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Security\Authentication\Controller\AbstractAuthenticationController;
use Neos\Flow\Security\Exception\AuthenticationRequiredException;
use Neos\Error\Messages\Error;

class LoginController extends AbstractAuthenticationController
{

    /**
     * ログイン画面表示
     * 
     * @return void
     */
    public function indexAction()
    {}


    /**
     * ログイン成功時の処理
     * 
     * @return string
     */
    protected function onAuthenticationSuccess(ActionRequest $originalRequest = null)
    {
        if ($originalRequest !== null) {
                $this->redirectToRequest($originalRequest);
        }
        $this->redirect('index', 'Top', 'Neos.Welcome');
    }

    /**
     * ログアウト時の処理
     *
     * @return void
     */
    public function logoutAction() {
        parent::logoutAction();
        $this->addFlashMessage('Logout successful');
        $this->redirect('index');
    }

    /**
     * 認証失敗時の処理
     *
     * @return void
     */
    protected function onAuthenticationFailure(AuthenticationRequiredException $exception = null)
    {
        $this->controllerContext->getFlashMessageContainer()->addMessage(new Error('Authentication failed!', ($exception === null ? 1347016771 : $exception->getCode())));
    }
}

HTML

HTMLは、Flow標準のテンプレートエンジン「Fluid」を用いて書きました。
formの送信先はLoginControllerのauthenticateActionメソッドを指定します。LoginControllerが継承しているAbstractAuthenticationControllerで定義されているActionメソッドです。

Login/index.html
<f:layout name="Default" />

<f:section name="Title">Login</f:section>

<f:section name="Content">
    <f:form controller="Login" action="authenticate" method="post">
        <p for="username">Username:</p>
        <f:form.textfield id="username" name="username" /><br/>

        <p for="password">Password:</p>
        <f:form.password id="password" name="password" /><br/>
        <f:form.submit value="Login" />
    </f:form>
</f:section>

画面キャプチャはこんな感じ。
ユーザ名とパスワードを入力するシンプルな画面です。

image.png

Sttings.yaml

前述のとおり、設定ファイルには認証プロバイダを記載します。
認証プロバイダ、トークン、エントリーポイントなど、必要な情報を記載します。

Settings.yaml
Neos:
  Flow:
    security:
      authentication:
        providers:
          DefaultProvider:
            provider: 'PersistedUsernamePasswordProvider'
            token: 'UsernamePassword'
            tokenOptions:
              usernamePostField: 'username'
              passwordPostField: 'password'
            entryPoint: 'WebRedirect'
            entryPointOptions:
              routeValues:
                '@package': 'Neos.Welcome'
                '@controller': 'Login'
                '@action': 'index'

providersは複数指定することが可能です。これにより、二段階認証なども実現できそうです。(試せてはないですが)

Top画面

ログイン後に開くTOP画面を作成します。
特に変わったところはないので、説明は割愛。

ソースはこちら
TopController.php
<?php
namespace Neos\Welcome\Controller;

use Neos\Flow\Mvc\Controller\ActionController;

class TopController extends ActionController
{
    public function indexAction()
    {
    }
}

Top/index.html
<f:layout name="Default" />
<f:section name="Title">TOP</f:section>
<f:section name="Content">
    <h1>TOP!!!</h1>
</f:section>

3. 動作確認

それでは、ログイン成功時と失敗時、それぞれを動作確認してみましょう。

3-1. ログイン成功

先ほど登録したアカウントでログインを試みます。

image.png

ログインできました!
image.png

3-2. ログイン失敗

続いて、ログイン失敗してみましょう。
パスワードに適当な文字を入れます。

image.png

ログインに失敗し、エラーメッセージとともに再度ログイン画面が表示されました。

image.png

今回は認証の処理を入れただけで、認可の処理は入れていません。そのため、ログインしなくてもTOP画面を開くことができます。

終わりに

認証というと難しいイメージがあったんですが、サクッと実装できました。
Flowの認証回りのソースも読み込んだので、もっと詳しい説明や認可処理についても別の記事で書きたいですね。

ここまで読んでいただきありがとうございました!

2
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
2
0