— y2sunlight 2020-09-03
目次
Apricotでは次の2つのユーザ認証をサポートしています。
セッション認証ではRemember-Meトークンによる自動ログインもサポートしています。
これらのユーザ認証の基盤はApricotコアに含まれています。Apricotのスケルトンではこの基盤を使って認証用のミドルウェアとコントローラーを実装しています。本章では、スケルトンでの認証に関する設定方法を示し、セッション認証用のミドルウェアとコントローラーの作成方法を説明します。
スケルトンでは、ユーザ認証用のミドルウェアとして、基本認証とセッション認証の2つが用意されています。ミドルウェアの設定は、アプリケーションの設定ファイル app.php
で行います。初期設定では、ユーザ認証にはセッション認証が使用されています。
/your-project/config
<?php return [ // ... 'middleware' =>[ \App\Middleware\Auth\SessionAuth::class, /* Session authentication */ ], // ... ];
ユーザ認証を基本認証に変更する場合は、ミドルウェアを以下のように変更して下さい。
'middleware' =>[ \App\Middleware\Auth\BasicAuth::class, /* Basic authentication */ ],
基本認証はWebサーバの機能を使ったログインページを作る必要のない認証方法です。比較的小規模で簡易的なアプリケーションの場合に選択することができます。
ユーザ認証の設定は、アプリケーションの設定ファイル(app.php)内の auth
要素で行います。
/your-project/config
<?php return [ // ... 'auth' =>[ 'db'=>[ 'user'=>[ 'account' =>'account', 'password' =>'password', 'remember' =>'remember_token', ], ], 'expires_sec'=> 2*7*24*3600, /* 2weekws */ 'menu'=> true, ], ];
auth.db
要素には以下のデータベースの設定を行います。
上の例では、user
テーブルの account
と password
カラムを使ってユーザ認証しています。また、RememberMeトークン保存用のカラムは remember
です。必要に応じてテーブル名及びカラム名は変更できます。ここで指定したテーブル名が認証用のユーザモデルになります。
その他に以下のがあります。
ユーザ認証の設定には、Authenticatable
インターフェースを実装したユーザモデルを作る事が必要です。このモデルのデータベース情報を、前項で説明した app.php
の auth.db
要素 に設定して下さい。Apricotのスケルトンでは認証用のユーザモデルとして User
クラスを作っています。
/your-project/config
namespace App\Models; use App\Foundation\Model; use ORM; use Apricot\Foundation\Security\Authenticatable; use App\Foundation\Security\AuthTrait; /** * User Model */ class User extends Model implements Authenticatable { /** * Includeing default implementation of Authenticatable interface. */ use AuthTrait; // ... }
AuthTrait
には Authenticatable
インターフェースが実装されています( 実際に app.php
の auth.db
要素 を使っているのは AuthTrait
です)。 従って、AuthTrait
を use するだけで、それ以上のコーディングは不要です。
AuthUserクラスは、Apricot コアの Authentication クラスをシングルトンにしたもので、全てのユーザ認証機能のこのクラスに集約されています。
AuthUserクラスでは以下のメソッドが使用できます。これらのメソッドは、主にミドルウェアと認証コントローラによって使用されますが、getUser()メソッドは様々な所から呼び出される可能性があります。
使用法: AuthUser::{メソッド}
メソッド | 機能 |
---|---|
authenticate (string $account, string $password, bool $remenber=false):bool | ユーザの認証を行い、成功の場合trueを返します。 (アカウントとパスワードで認証します) |
remember():bool | ユーザの自動認証を行い、成功の場合trueを返します。 (RememberMeトークンによる自動ログインで使用します) |
check():bool | ユーザが認証されているか否かを返します。 |
verify():bool | ユーザが認証されているか否かを返します。 認証されている場合、ログインユーザ情報の更新を行います。 |
forget() | ログインセッションを削除します。 |
getUser():object | ログインユーザを返します。 |
getPathAfterLogin():string | ログイン後のURLパスを返します。 |
Apricotのスケルトンでは、以下のように AuthUser
クラスが実装されています。
/your-project/app/Foundation/Security
<?php namespace App\Foundation\Security; use Core\Foundation\Singleton; use Core\Foundation\Security\Authentication; use App\Models\User; class AuthUser extends Singleton { /** * Create user authentication instance. * @return \Core\Foundation\Security\Authentication */ protected static function createInstance() { return new Authentication(new User()); } }
createInstance()
では、ユーザモデル( User )を使って、Authentication
クラスのシングルトンを生成しています。同様の方法で別のユーザモデルを使用した Authentication
クラスのシングルトンも同時に作る事ができます。例えば、Userとは別に、AdminUser クラスや ApiUser クラスを使用した認証などが考えられます。
このように、Apricotのスケルトンでは潜在的にマルチ認証を想定した実装ができるように配慮しています。但し、マルチ認証を行う場合は、ミドルウェアと認証コントローラを追加またはカスタマイズする必要があります。
セッション認証のミドルウェアは、/App/Middleware/Auth/SessionAuth
です。他のミドルウェアと同様に、middlewareインターフェースの process() メソッドを実装し、その中で認証処理を行っています。
以下に、SessionAuth
ミドルウェアがどのようにAuthUserクラスを使って認証処理を実行しているのかについて説明します。
/your-project/app/Middleware/Auth
/** * Middleware - Session authentication */ class SessionAuth implements Middleware { /** * @var array List of controllers to exclude */ private $exclude = [ 'AuthController', ]; /** * {@inheritDoc} * @see \Apricot\Foundation\Middleware\Middleware::invoke() */ public function process(Invoker $next): Response { // When excluded controller. if (in_array(controllerName(), $this->exclude)) { return $next->invoke(); } // Verifys whether user is authenticated. if (AuthUser::verify()) { return $next->invoke(); } // Redirect to login page If not authenticated. return redirect(route('login')); } }
$this->exclude 配列に含まれているコントローラは認証から除外します。通常は、ここには認証コントローラ( 上例では AuthController
)が含まれます。他には、公開のページやWebAPIなどが含まれます。
AuthUser::verify()
でユーザが認証されているか否かを調べ、認証されていれば次の処理に制御を渡します。まだユーザ認証されていない場合は、ヘルパー関数 redirect() を呼び出してログインページ画面にリダイレクトします。
ミドルウェアでは、認証のチェックに AuthUser::check()
ではなくて、AuthUser::verify()
を使用するのは、後者のメソッドがログインユーザ情報の更新を行うからです。これによって AuthUser
は常に最新のログインユーザ情報を保持することができます。
基本認証のミドルウェアは、/App/Middleware/Auth/BasicAuth
です。このクラスは以下に配置されていますので、必要に応じて参照して下さい。
/your-project/app/Middleware/Auth/BasicAuth.php
セッション認証を使用した場合の認証コントローラについて説明します。Airpcotのスケルトンでは、認証コントローラとして App\Controllers\AuthController
を提供しています。
AuthController
のルーティングは以下のように定義されています。
/your-project/config
$r->get ('/login', 'AuthController@showForm'); $r->post('/login', 'AuthController@login'); $r->get ('/logout', 'AuthController@logout');
セッション認証ではログインページから入力されるユーザアカウントとパスワードでユーザを認証します。また、ログイン時に、ブラウザ終了後もログイン状態を保持するか否かのチェック( Remember-Me )を行うことができます。
これらの処理は、AuthController
クラスによって実行されます。以下に、このクラスのアクションがどのようにAuthUserクラスを使ってどのように認証処理を実行しているのかについて説明します。
public function showForm() { if (AuthUser::check()) { // Redirects to the top page if authenticated return redirect(route('')); } if (AuthUser::remember()) { // Redirects to the top page display if automatic authentication is possible return redirect(route('')); } return render('login'); }
AuthUser::check()
でユーザが認証されているか否かのチェックを行い、成功の場合は、ヘルパー関数 redirect() でトップ画面にリダイレクトします。
一方、AuthUser::check()
が失敗した場合は、AuthUser::remember()
で自動ログインを試みます。これが成功した場合は、トップ画面にリダイレクトします。
ユーザセッションもなく、自動ログインにも失敗した場合、コントローラーはログインページをレンダリングします。
public function login() { $inputs = Input::all(); if (!AuthUser::authenticate($inputs['account'], $inputs['password'], !empty($inputs['remember']))) { // If the user cannot be found $errorBag = new ErrorBag([__('auth.login.error.no_account')]); return redirect(back())->withInputs()->withErrors($errorBag); } // When login is successful return redirect(AuthUser::getPathAfterLogin()); }
TODO:
まず、Input::all()でログインページから入力変数を取得します。そして、それらの変数をパラメータにして AuthUser::authenticate()
でユーザ認証を行います。認証に成功した場合、AuthUser::getPathAfterLogin()
でログイン後のに遷移するページのパスを取得して、そこにリダイレクトします。
認証に失敗した場合は、withInputs()で入力変数を、withErrors()でバリデーションのエラーバッグを、それぞれのフラッシュ変数に保存し、ログインページにリダイレクトします。
public function logout() { // Destroys the session AuthUser::forget(); // Redirects to the login page return redirect(route("login")); }
AuthUser::forget()
で認証セッションを破棄し、ログインページにリダイレクトします。
Apricotのコアで提供するユーザ認証機能を以下に示します。これらは、アプリケーションのモデルやデータベースに依存しない基本的なもので、ミドルウェアや認証コントローラーなどで使用されます。
Apricotコアのユーザ認証機能を使用するには、Authenticatable
インターフェースを実装したモデルを作成する必要があります。
完全修飾名 : Apricot\Foundation\Security\Authenticatable
メソッド | 機能 |
---|---|
getAuthName():string | 認証を識別できる任意の名前を返します。 通常は認証のモデルとなるテーブル名を返します。(例:user) |
authenticateUser (string $account, string $password) :object|bool | ユーザの認証を行います。 成功の場合ユーザオブジェクトを返し、他は false を返します。 (通常はアカウントとパスワードによる認証を行います) |
remember (string $remenber_token) :object|bool | ユーザのRemnber-Meトークンによる認証を行います。 成功の場合ユーザオブジェクトを返し、他は false を返します。 (RememberMeトークンによる自動ログインで使用します) |
retrieveUser(object $user) :object|bool | ユーザオブジェクトの再検索を行います。 成功の場合ユーザオブジェクトを返し、他は false を返します。 再検索のキーは引数 $user のアカウントが使えます。 |
saveRemenberToken (object $user, string $remenber_token) :bool | ユーザのRemnber-Meトークンを保存します。 保存時のキーは引数 $user のアカウントが使えます。 |
Apricotのスケルトンでは、Authenticatable
インターフェースを実装した AuthTrait
を使用することができるので、そちらを利用して下さい:
/your-project/app/Foundation/Security/AuthTrait.php
Authenticationクラスはユーザ認証を行う全ての機能を持っています。通常、このクラスは、ミドルウェアと認証コントローラで使用されます。
完全修飾名 : Apricot\Foundation\Security\Authentication
メソッド | 機能 |
---|---|
__construct (Authenticatable $auth) | Authenticationのコンストラクタ 生成時にAuthenticatableインターフェースを与えます。 |
authenticate (string $account, string $password, bool $remenber=false):bool | ユーザの認証を行い、成功の場合trueを返します。 (アカウントとパスワードで認証します) |
remember():bool | ユーザの自動認証を行い、成功の場合trueを返します。 (RememberMeトークンによる自動ログインで使用します) |
check():bool | ユーザが認証されているか否かを返します。 |
verify():bool | ユーザが認証されているか否かを返します。 認証されている場合、ログインユーザ情報の更新を行います。 |
forget() | ログインセッションを削除します。 |
getUser():object | ログインユーザを返します。 |
getPathAfterLogin():string | ログイン後のURLパスを返します。 |
本クラスは基本的にセッション認証用に作られていますが、基本認証の場合も authenticate()、verify()、getUser()メソッドは使用できます(基本認証の認証ページはブラウザによって提供され、ログアウトという考え方はなく、セッションとログイン状態は常に一致します)。また、check() と verify() は似たようなメソッドですが、check()はコントローラのアクションで、verify()はミドルウェアでの使用を想定しています。