このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン 次のリビジョン 両方とも次のリビジョン | ||
apricot:usage:ja:middleware [2020/08/03 11:52] tanaka [ミドルウェアの構成] |
apricot:usage:ja:middleware [2020/08/24 12:30] y2sunlight [CSRF対策] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | > 編集中 | ||
- | |||
- | ---- | ||
- | |||
====== Apricot ミドルウェア ====== | ====== Apricot ミドルウェア ====== | ||
--- // | --- // | ||
- | [[apricot: | + | [[apricot: |
目次 | 目次 | ||
+ | * [[apricot: | ||
* [[apricot: | * [[apricot: | ||
+ | * [[apricot: | ||
* [[apricot: | * [[apricot: | ||
* [[apricot: | * [[apricot: | ||
- | * [[apricot: | + | * [[apricot: |
+ | * [[apricot: | ||
* Apricot ミドルウェア | * Apricot ミドルウェア | ||
* [[apricot: | * [[apricot: | ||
- | * [[apricot: | + | * [[apricot: |
* [[apricot: | * [[apricot: | ||
---- | ---- | ||
- | ===== ミドルウェアの構成 ===== | + | ===== ミドルウェアの構造 ===== |
- | 本章ではミドルウェア基盤を作ります。ミドルウェアとはアクションを囲んでいる層のような存在で、ユーザからのリクエストは何層もあるミドルウェアを通って最終的にアクションにたどり着きそこでレスポンスが生成されますが、途中でリクエストが中断され、ミドルウェアがレスポンスを生成することもあります。これを図示すると以下のようになります。 | + | ミドルウェアとはアクションを囲んでいる層のような存在で、利用者からのリクエストは何層もあるミドルウェアを通って最終的にアクションにたどり着きそこでレスポンスが生成されますが、途中でリクエストが中断され、ミドルウェアがレスポンスを生成することもあります。また、ミドルウェアとは途中でリクエストやレスポンスに介入してデータをモニタリング、フィルタリングまたは変換したりすることもできます。 |
- | ==== ミドルウェア構造 ==== | + | === ミドルウェアパイプライン |
- | {{:apricot:ext: | + | |
+ | {{:apricot:usage: | ||
上図のような処理のネスト構造を '' | 上図のような処理のネスト構造を '' | ||
- | Apricotには以下のミドルウェアが実装されています。 | + | ミドルウェアパイプラインを含めたミドルウェアの仕組みはApricotのコアが提供しますが、具体的なミドルウェアの実装はアプリ側に任されています。 |
- | * [[apricot: | + | \\ |
- | * [[apricot: | + | |
- | * [[apricot: | + | |
- | ミドルウェアパイプラインを含めたミドルウェアの仕組みはApricotのコアの機能として実装しますが、上記のような具体的なミドルウェアの実装はアプリ側で行います。 | + | ===== Middleware |
- | ==== Middleware インターフェース | + | 全てのミドルウェアは以下の '' |
- | 冒頭の[[# | ||
- | |||
- | {{fa> | ||
<code php Middleware.php> | <code php Middleware.php> | ||
<?php | <?php | ||
- | namespace | + | namespace |
- | use Core\Foundation\Invoker; | + | use Apricot\Foundation\Invoker; |
- | use Core\Foundation\Response; | + | use Apricot\Foundation\Response; |
/** | /** | ||
行 56: | 行 51: | ||
{ | { | ||
/** | /** | ||
- | | + | |
- | * @param Invoker $next Next invoker | + | * |
- | * @return \Core\Foundation\Response if return response, then don'true call next action | + | * @param Invoker $next Next invoker. |
+ | * @return \Apricot\Foundation\Response if return | ||
*/ | */ | ||
public function process(Invoker $next) :Response; | public function process(Invoker $next) :Response; | ||
} | } | ||
</ | </ | ||
+ | |||
+ | 前項のミドルウェア構造の図から分かるように、ミドルウェアのインターフェースの役割は自分を処理の後に次のプロセッサーに制御を渡すことです。この為に、ミドルウェアパイプラインは、'' | ||
+ | |||
+ | <code php Invoker.php> | ||
+ | <?php | ||
+ | namespace Apricot\Foundation; | ||
+ | |||
+ | /** | ||
+ | * Invoker Interface | ||
+ | */ | ||
+ | interface Invoker | ||
+ | { | ||
+ | /** | ||
+ | * Invokes an incoming request processor | ||
+ | * | ||
+ | * @return \Apricot\Foundation\Response | ||
+ | */ | ||
+ | public function invoke() : Response; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | '' | ||
\\ | \\ | ||
- | ===== アクセスログ | + | ===== Middlewareの実装 ===== |
- | >TODO | + | |
+ | Apricotのスケルトンで提供されているミドルウェアは、以下場所にの配置してあります。この配置は必須ではありません。適宜アプリケーションのルールで変更して下さい。 | ||
+ | |||
+ | < | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | 以下は、ミドルウェアの典型的な実装例です。 | ||
+ | |||
+ | {{fa> | ||
+ | <code php Invoker.php> | ||
+ | <?php | ||
+ | namespace App\Middleware; | ||
+ | |||
+ | use Apricot\Foundation\Response; | ||
+ | use Apricot\Foundation\Invoker; | ||
+ | use Apricot\Foundation\Middleware\Middleware; | ||
+ | |||
+ | /** | ||
+ | * Middleware | ||
+ | */ | ||
+ | class MyMiddleware implements Middleware | ||
+ | { | ||
+ | /** | ||
+ | * {@inheritDoc} | ||
+ | * @see \Apricot\Foundation\Middleware\Middleware:: | ||
+ | */ | ||
+ | public function process(Invoker $next): Response | ||
+ | { | ||
+ | // Pre-processing | ||
+ | // ... | ||
+ | |||
+ | // Calls the next Invoker. | ||
+ | $response | ||
+ | |||
+ | // Post-processing | ||
+ | // ... | ||
+ | |||
+ | return response | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Middlewareの設定 | ||
+ | |||
+ | 新しく作成したミドルウェアは、アプリケーションの設定ファイル ''/ | ||
+ | |||
+ | {{fa>folder-open-o}} ** / | ||
+ | <code php app.php> | ||
+ | <?php | ||
+ | /** | ||
+ | * This file contains application settings. | ||
+ | */ | ||
+ | return | ||
+ | [ | ||
+ | ' | ||
+ | \App\Middleware\AccessLog:: | ||
+ | \App\Middleware\VerifyCsrfToken:: | ||
+ | \App\Middleware\Auth\SessionAuth:: | ||
+ | \App\Middleware\InputConverter:: | ||
+ | \App\Middleware\MyMiddleware :: | ||
+ | ], | ||
+ | ]; | ||
+ | </ | ||
+ | |||
+ | これらのミドルウェアは、パイプラインによって登録した順に発動されます。 | ||
\\ | \\ | ||
- | ===== 認証 | + | ===== ミドルウエアの実例 |
- | >TODO | + | |
+ | Apricotのスケルトンでは以下のミドルウェアが初期実装されています。 | ||
+ | |||
+ | * アクセスログ | ||
+ | * フォーム入力変換 | ||
+ | * CSRF対策 | ||
+ | * ユーザ認証(基本認証 及び セッション認証) | ||
+ | |||
+ | ここでは、ユーザ認証以外のミドルウェアについて説明します。ユーザ認証については後続の章をご覧ください。これらのミドルウェアを適用したくない場合は、アプリケーションの設定ファイル( config/ | ||
\\ | \\ | ||
- | ===== CSRF ===== | + | ==== アクセスログ |
- | >TODO | + | |
+ | アクセスログのミドルウェアは、リクエストをモニタして次のプロセスを発動するだけの簡単な構造をしています。アクセスログをカスタマイズするには、このミドルウェアを修正して下さい。 | ||
+ | |||
+ | {{fa> | ||
+ | <code php AccessLog.php> | ||
+ | /** | ||
+ | * Access Log - Middleware | ||
+ | */ | ||
+ | class AccessLog implements Middleware | ||
+ | { | ||
+ | /** | ||
+ | * {@inheritDoc} | ||
+ | * @see \Apricot\Foundation\Middleware\Middleware:: | ||
+ | */ | ||
+ | public function process(Invoker $next): Response | ||
+ | { | ||
+ | // Logs a message. | ||
+ | $message | ||
+ | |||
+ | // Logs context data. | ||
+ | $data = [ | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ]; | ||
+ | Log:: | ||
+ | |||
+ | // Calls the next Invoker. | ||
+ | return $next-> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 収集したログデータはinfoレベルで出力します。ログインユーザのアカウントを取得する為に、'' | ||
\\ | \\ | ||
+ | ==== フォーム入力変換 ==== | ||
+ | |||
+ | このミドルウェアの目的は、フォームの入力変数を変換することです。 | ||
+ | |||
+ | * 入力値をトリミングします。 | ||
+ | * 入力値が空の場合、その値をnullにします。 | ||
+ | |||
+ | これらの変換を望まない変数については、そのキーを '' | ||
+ | |||
+ | {{fa> | ||
+ | <code php InputConverter.php> | ||
+ | /** | ||
+ | * Input Converter - Middleware | ||
+ | */ | ||
+ | class InputConverter implements Middleware | ||
+ | { | ||
+ | /** | ||
+ | * @var array List of input variables to exclude | ||
+ | */ | ||
+ | private $exclude = [ | ||
+ | ' | ||
+ | ' | ||
+ | ]; | ||
+ | |||
+ | /** | ||
+ | * {@inheritDoc} | ||
+ | * @see \Apricot\Foundation\Middleware\Middleware:: | ||
+ | */ | ||
+ | public function process(Invoker $next): Response | ||
+ | { | ||
+ | $inputs = Input:: | ||
+ | foreach($inputs as $key=> | ||
+ | { | ||
+ | if (in_array($key, | ||
+ | if (is_string($value)) | ||
+ | { | ||
+ | $value = trim($value); | ||
+ | if($value === '' | ||
+ | Input:: | ||
+ | } | ||
+ | } | ||
+ | return $next-> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== CSRF対策 ==== | ||
+ | |||
+ | このミドルウェアの目的は、CSRF対策です。発行しているCSRFトークンの検証に失敗した場合は、'' | ||
+ | |||
+ | CSR対策を望まないコントローラーについては、そのクラス名を '' | ||
+ | |||
+ | {{fa> | ||
+ | <code php VerifyCsrfToken.php> | ||
+ | /** | ||
+ | * CSRF token verification - Middleware | ||
+ | */ | ||
+ | class VerifyCsrfToken implements Middleware | ||
+ | { | ||
+ | /** | ||
+ | * @var array List of controllers to exclude | ||
+ | */ | ||
+ | private $exclude = [ | ||
+ | ' | ||
+ | ]; | ||
+ | |||
+ | /** | ||
+ | * {@inheritDoc} | ||
+ | * @see \Apricot\Foundation\Middleware\Middleware:: | ||
+ | */ | ||
+ | public function process(Invoker $next): Response | ||
+ | { | ||
+ | if (!in_array(controllerName(), | ||
+ | { | ||
+ | // Verifies CSRF tokens. | ||
+ | if (!CsrfToken:: | ||
+ | { | ||
+ | throw new \Apricot\Exceptions\TokenMismatchException(' | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Generates a CSRF token. | ||
+ | CsrfToken:: | ||
+ | |||
+ | return $next-> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | '' | ||
+ | |||
+ | ^メソッド^機能^ | ||
+ | |generate()|セッション内のCSRFトークンが未生成の場合、それを生成してセッションに格納します。| | ||
+ | |verify(): | ||
+ | |||
+ | フォーム内にCSRFトークンを入力変数として埋め込む方法については、「[[apricot: | ||
+ | |||
+ | アプリケーション設定( [[apricot: | ||
+ | |||
+ | \\ | ||