====== Apricot ユーザ認証 ====== --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-09-03// [[apricot:usage:ja|Apricot ドキュメント に戻る]] 目次 * [[apricot:usage:ja:features|Apricot 特徴と概要]] * [[apricot:usage:ja:config|Apricot 配置と構成]] * [[apricot:usage:ja:errors-logging|Apricot ログとエラー処理]] * [[apricot:usage:ja:http|Apricot リクエストとレスポンス]] * [[apricot:usage:ja:frontend|Apricot フロントエンド]] * [[apricot:usage:ja:database|Apricot データベース]] * [[apricot:usage:ja:model|Apricot モデルとサービス]] * [[apricot:usage:ja:middleware|Apricot ミドルウェア]] * [[apricot:usage:ja:controller|Apricot コントローラ]] * [[apricot:usage:ja:validation|Apricot バリデーション]] * [[apricot:usage:ja:provider|Apricot サービスプロバイダー]] * Apricot ユーザ認証 * [[apricot:usage:ja:utility|Apricot ユーティリティ]] ---- Apricotでは次の2つのユーザ認証をサポートしています。 * 基本認証 --- HTTPで定義される[[https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC|認証方法]] * セッション認証 --- ログイン画面による認証で、ログイン状態をセッションで管理する方式 セッション認証ではRemember-Meトークンによる自動ログインもサポートしています。 これらのユーザ認証の基盤はApricotコアに含まれています。Apricotのスケルトンではこの基盤を使って認証用のミドルウェアとコントローラーを実装しています。本章では、スケルトンでの認証に関する設定方法を示し、セッション認証用のミドルウェアとコントローラーの作成方法を説明します。 \\ ===== ユーザ認証の設定 ===== ==== 認証方法の設定 ==== スケルトンでは、ユーザ認証用のミドルウェアとして、基本認証とセッション認証の2つが用意されています。ミドルウェアの設定は、アプリケーションの設定ファイル ''app.php'' で行います。初期設定では、ユーザ認証にはセッション認証が使用されています。 {{fa>folder-open-o}} ** /your-project/config ** [ \App\Middleware\Auth\SessionAuth::class, /* Session authentication */ ], // ... ]; ユーザ認証を基本認証に変更する場合は、ミドルウェアを以下のように変更して下さい。 'middleware' =>[ \App\Middleware\Auth\BasicAuth::class, /* Basic authentication */ ], 基本認証はWebサーバの機能を使ったログインページを作る必要のない認証方法です。比較的小規模で簡易的なアプリケーションの場合に選択することができます。 \\ ==== ユーザ認証の設定 ==== ユーザ認証の設定は、アプリケーションの設定ファイル(app.php)内の ''auth'' 要素で行います。 {{fa>folder-open-o}} ** /your-project/config** [ 'db'=>[ 'user'=>[ 'account' =>'account', 'password' =>'password', 'remember' =>'remember_token', ], ], 'expires_sec'=> 2*7*24*3600, /* 2weekws */ 'menu'=> true, ], ]; ''auth.db'' 要素には以下のデータベースの設定を行います。 * user --- テーブル名 * account --- アカウントのカラム名 * password --- パスワードのカラム名 * remember --- RememberMeトークンのカラム名 上の例では、''user'' テーブルの ''account'' と ''password'' カラムを使ってユーザ認証しています。また、RememberMeトークン保存用のカラムは ''remember'' です。必要に応じてテーブル名及びカラム名は変更できます。ここで指定したテーブル名が[[#認証用のユーザモデルの作成|認証用のユーザモデル]]になります。 その他に以下のがあります。 * auth.expires_sec --- RememberMeトークンクッキーの保存期間を秒単位で指定 * auth.menu --- ページの左上にユーザメニューを表示するか否か設定(デフォルト値:false) \\ ==== 認証用のユーザモデルの作成 ==== ユーザ認証の設定には、''Authenticatable'' インターフェースを実装したユーザモデルを作る事が必要です。このモデルのデータベース情報を、[[#認証方法の設定|前項]]で説明した ''app.php'' の ''auth.db'' 要素 に設定して下さい。Apricotのスケルトンでは認証用のユーザモデルとして ''User'' クラスを作っています。 {{fa>folder-open-o}} ** /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クラス ===== AuthUserクラスは、Apricot コアの [[#authenticationクラス|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パスを返します。| \\ === AuthUserクラスの実装 === Apricotのスケルトンでは、以下のように ''AuthUser'' クラスが実装されています。 {{fa>folder-open-o}} ** /your-project/app/Foundation/Security ** ''createInstance()'' では、ユーザモデル( User )を使って、''Authentication'' クラスのシングルトンを生成しています。同様の方法で別のユーザモデルを使用した ''Authentication'' クラスのシングルトンも同時に作る事ができます。例えば、Userとは別に、AdminUser クラスや ApiUser クラスを使用した認証などが考えられます。 このように、Apricotのスケルトンでは潜在的にマルチ認証を想定した実装ができるように配慮しています。但し、マルチ認証を行う場合は、ミドルウェアと認証コントローラを追加またはカスタマイズする必要があります。 \\ ===== ミドルウェア ===== ==== セッション認証 ==== セッション認証のミドルウェアは、''/App/Middleware/Auth/SessionAuth'' です。他のミドルウェアと同様に、[[apricot:usage:ja:middleware#middleware_インターフェース|middlewareインターフェース]]の process() メソッドを実装し、その中で認証処理を行っています。 以下に、''SessionAuth'' ミドルウェアがどのように[[#AuthUserクラス]]を使って認証処理を実行しているのかについて説明します。 {{fa>folder-open-o}} ** /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'' の[[apricot:usage:ja:controller#ルーティング]]は以下のように定義されています。 {{fa>folder-open-o}} ** /your-project/config ** $r->get ('/login', 'AuthController@showForm'); $r->post('/login', 'AuthController@login'); $r->get ('/logout', 'AuthController@logout'); * /login (GET) --- ログインページ表示 * /login (POST) --- ログイン処理 ( ユーザ認証 ) * /logout --- ログアウト処理 \\ ==== AuthControllerクラス ==== セッション認証ではログインページから入力されるユーザアカウントとパスワードでユーザを認証します。また、ログイン時に、ブラウザ終了後もログイン状態を保持するか否かのチェック( Remember-Me )を行うことができます。 これらの処理は、''AuthController'' クラスによって実行されます。以下に、このクラスのアクションがどのように[[#AuthUserクラス]]を使ってどのように認証処理を実行しているのかについて説明します。 === showFormアクション === 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()'' で自動ログインを試みます。これが成功した場合は、トップ画面にリダイレクトします。 ユーザセッションもなく、自動ログインにも失敗した場合、コントローラーはログインページをレンダリングします。 \\ === loginアクション === 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()でバリデーションのエラーバッグを、それぞれのフラッシュ変数に保存し、ログインページにリダイレクトします。 \\ === logoutアクション === public function logout() { // Destroys the session AuthUser::forget(); // Redirects to the login page return redirect(route("login")); } ''AuthUser::forget()'' で認証セッションを破棄し、ログインページにリダイレクトします。 \\ ===== コアのユーザ認証機能 ===== Apricotのコアで提供するユーザ認証機能を以下に示します。これらは、アプリケーションのモデルやデータベースに依存しない基本的なもので、ミドルウェアや認証コントローラーなどで使用されます。 * Authenticatable インターフェース --- 認証されるユーザモデルが持つべきインターフェース * Authentication クラス --- ユーザ認証機能を持ったクラス \\ ==== Authenticatableインターフェース ==== 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クラス ==== 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()はミドルウェアでの使用を想定しています。 \\