Nobuo Kihara
10 years ago
7 changed files with 163 additions and 9 deletions
@ -0,0 +1,114 @@
|
||||
認証 |
||||
==== |
||||
|
||||
ウェブアプリケーションとは異なり、RESTful API は通常はステートレスです。 |
||||
これは、セッションやクッキーは使用すべきでないことを意味します。 |
||||
従って、ユーザの認証ステータスをセッションやクッキーで保持することが出来ないため、全てのリクエストに何らかの認証情報を付加する必要があります。 |
||||
通常使われるのは、ユーザを認証するための秘密のアクセストークンを全てのリクエストとともに送信する方法です。 |
||||
アクセストークンはユーザを一意に特定して認証することが出来るものですので、**API リクエストは、中間者攻撃 (man-in-the-middle attack) を防止するために、常に HTTPS 経由で送信されなければなりません**。 |
||||
|
||||
アクセストークンを送信するには、いくつかの異なる方法があります。 |
||||
|
||||
* [HTTP Basic 認証](http://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC): アクセストークンはユーザ名として送信されます。 |
||||
この方法は、アクセストークンを API コンシューマ側で安全に保存することが出来る場合、例えば API コンシューマがサーバ上で走るプログラムである場合などにのみ使用されるべきです。 |
||||
* クエリパラメータ: アクセストークンは API の URL、例えば、`https://example.com/users?access-token=xxxxxxxx` でクエリパラメータとして送信されます。 |
||||
ほとんどのウェブサーバはクエリパラメータをサーバのログに記録するため、この手法は、アクセストークンを HTTP ヘッダを使って送信することができない `JSONP` リクエストに応答するために主として使用されるべきです。 |
||||
* [OAuth 2](http://oauth.net/2/): OAuth2 プロトコルに従って、アクセストークンはコンシューマによって権限付与サーバから取得され、[HTTP Bearer Tokens](http://tools.ietf.org/html/rfc6750) 経由で API サーバに送信されます。 |
||||
|
||||
Yii は上記の全ての認証方法をサポートしています。新しい認証方法を作成することも簡単に出来ます。 |
||||
|
||||
あなたの API に対して認証を有効にするためには、次のステップを実行します。 |
||||
|
||||
1. `user` アプリケーションコンポーネントを構成します。 |
||||
- [[yii\web\User::enableSession|enableSession]] プロパティを `false` に設定します。 |
||||
- [[yii\web\User::loginUrl|loginUrl]] プロパティを `null` に設定し、ログインページにリダイレクトする代りに HTTP 403 エラーを表示します。 |
||||
2. REST コントローラクラスにおいて、`authenticator` ビヘイビアを構成することによって、どの認証方法を使用するかを指定します。 |
||||
3. [[yii\web\User::identityClass|ユーザアイデンティティクラス]] において [[yii\web\IdentityInterface::findIdentityByAccessToken()]] を実装します。 |
||||
|
||||
ステップ 1 は必須ではありませんが、ステートレスであるべき RESTful API のために推奨されます。 |
||||
[[yii\web\User::enableSession|enableSession]] が false である場合、ユーザの認証ステータスがセッションを使ってリクエストをまたいで存続することはありません。 |
||||
その代りに、すべてのリクエストに対して認証が実行されます。このことは、ステップ 2 と 3 によって達成されます。 |
||||
|
||||
> Tip|情報: RESTful API をアプリケーションの形式で開発する場合は、アプリケーションの構成情報で `user` アプリケーションコンポーネントの [[yii\web\User::enableSession|enableSession]] プロパティを構成することが出来ます。 |
||||
RESTful API をモジュールとして開発する場合は、次のように、モジュールの `init()` メソッドに一行を追加することが出来ます。 |
||||
> ```php |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
\Yii::$app->user->enableSession = false; |
||||
} |
||||
``` |
||||
|
||||
例えば、HTTP Basic 認証を使う場合は、`authenticator` ビヘイビアを次のように構成することが出来ます。 |
||||
|
||||
```php |
||||
use yii\filters\auth\HttpBasicAuth; |
||||
|
||||
public function behaviors() |
||||
{ |
||||
$behaviors = parent::behaviors(); |
||||
$behaviors['authenticator'] = [ |
||||
'class' => HttpBasicAuth::className(), |
||||
]; |
||||
return $behaviors; |
||||
} |
||||
``` |
||||
|
||||
上で説明した三つの認証方法を全てサポートしたい場合は、次のように `CompositeAuth` を使うことが出来ます。 |
||||
|
||||
```php |
||||
use yii\filters\auth\CompositeAuth; |
||||
use yii\filters\auth\HttpBasicAuth; |
||||
use yii\filters\auth\HttpBearerAuth; |
||||
use yii\filters\auth\QueryParamAuth; |
||||
|
||||
public function behaviors() |
||||
{ |
||||
$behaviors = parent::behaviors(); |
||||
$behaviors['authenticator'] = [ |
||||
'class' => CompositeAuth::className(), |
||||
'authMethods' => [ |
||||
HttpBasicAuth::className(), |
||||
HttpBearerAuth::className(), |
||||
QueryParamAuth::className(), |
||||
], |
||||
]; |
||||
return $behaviors; |
||||
} |
||||
``` |
||||
|
||||
`authMethods` の各要素は、認証方法クラスの名前であるか、構成情報配列でなければなりません。 |
||||
|
||||
|
||||
`findIdentityByAccessToken()` の実装はアプリケーション固有のものです。 |
||||
例えば、各ユーザが一つだけアクセストークンを持ち得るような単純なシナリオでは、アクセストークンをユーザのテーブルの `access_token` カラムに保存することが出来ます。 |
||||
そうすれば、次のように、`findIdentityByAccessToken()` メソッドを `User` クラスにおいて簡単に実装することが出来ます。 |
||||
|
||||
```php |
||||
use yii\db\ActiveRecord; |
||||
use yii\web\IdentityInterface; |
||||
|
||||
class User extends ActiveRecord implements IdentityInterface |
||||
{ |
||||
public static function findIdentityByAccessToken($token, $type = null) |
||||
{ |
||||
return static::findOne(['access_token' => $token]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
上記のように認証が有効化された後は、全ての API リクエストに対して、リクエストされたコントローラが `beforeAction()` の段階でユーザを認証することを試みます。 |
||||
|
||||
認証が成功すると、コントローラはその他のチェック (レート制限、権限付与など) をしてから、アクションを実行します。 |
||||
認証されたユーザのアイデンティティは `Yii::$app->user->identity` によって取得することが出来ます。 |
||||
|
||||
認証が失敗したときは、HTTP ステータス 401 およびその他の適切なヘッダ (HTTP Basic 認証に対する `WWW-Authenticate` ヘッダなど) を持つレスポンスが送り返されます。 |
||||
|
||||
|
||||
## 権限付与 <a name="authorization"></a> |
||||
|
||||
ユーザが認証された後、リクエストされたリソースに対してリクエストされたアクションを実行する許可を彼または彼女が持っているかどうかをチェックしたい場合があるでしょう。 |
||||
*権限付与* と呼ばれるこのプロセスについては、[権限付与](security-authorization.md) のセクションで詳細に説明されています。 |
||||
|
||||
あなたのコントローラが [[yii\rest\ActiveController]] から拡張したものである場合は、[[yii\rest\Controller::checkAccess()|checkAccess()]] メソッドをオーバーライドして権限付与のチェックを実行することが出来ます。 |
||||
このメソッドが [[yii\rest\ActiveController]] によって提供されている内蔵のアクションから呼び出されます。 |
@ -0,0 +1,40 @@
|
||||
レート制限 |
||||
========== |
||||
|
||||
悪用を防止するために、あなたの API に *レート制限* を加えることを検討すべきです。 |
||||
例えば、各ユーザの API 使用を 10 分間で最大 100 回までの API 呼び出しに制限したいとしましょう。 |
||||
ユーザから上記の期間内に多すぎるリクエストを受け取った場合は、ステータスコード 429 (「リクエストが多すぎる」の意味) を持つレスポンスを返すのです。 |
||||
|
||||
レート制限を可能にするためには、[[yii\web\User::identityClass|ユーザアイデンティティクラス]] で [[yii\filters\RateLimitInterface]] を実装しなければなりません。 |
||||
このインタフェイスは次の三つのメソッドを実装することを要求します。 |
||||
|
||||
* `getRateLimit()`: 許可されているリクエストの最大数と期間を返します |
||||
(例えば、`[100, 600]` は 600 秒間に最大 100 回の API 呼び出しが出来ることを意味します)。 |
||||
* `loadAllowance()`: 許可されているリクエストの残り数と、レート制限が最後にチェックされたときの対応する UNIX タイムスタンプを返します。 |
||||
* `saveAllowance()`: 許可されているリクエストの残り数と現在の UNIX タイムスタンプの両方を保存します。 |
||||
|
||||
許容されているリクエスト数とタイムスタンプの情報を記録するために、ユーザのテーブルの二つのカラムを使うことが出来ます。 |
||||
それらを定義すれば、`loadAllowance()` と `saveAllowance()` は、認証された現在のユーザに対応する二つのカラムの値を読み書きするものとして実装することが出来ます。 |
||||
パフォーマンスを向上させるために、これらの情報の断片をキャッシュや NoSQL ストレージに保存することを検討しても良いでしょう。 |
||||
|
||||
アイデンティティのクラスに必要なインタフェイスを実装すると、Yii は [[yii\rest\Controller]] のアクションフィルタとして構成された [[yii\filters\RateLimiter]] を使って、自動的にレート制限のチェックを行うようになります。 |
||||
レート制限を超えると、レートリミッタが [[yii\web\TooManyRequestsHttpException]] を投げます。 |
||||
|
||||
レートリミッタは、REST コントローラクラスの中で、次のようにして構成することが出来ます。 |
||||
|
||||
```php |
||||
public function behaviors() |
||||
{ |
||||
$behaviors = parent::behaviors(); |
||||
$behaviors['rateLimiter']['enableRateLimitHeaders'] = false; |
||||
return $behaviors; |
||||
} |
||||
``` |
||||
|
||||
レート制限が有効にされると、デフォルトでは、送信される全てのレスポンスに、現在のレート制限の情報を含む次の HTTP ヘッダが付加されます。 |
||||
|
||||
* `X-Rate-Limit-Limit` - 一定期間内に許可されるリクエストの最大数 |
||||
* `X-Rate-Limit-Remaining` - 現在の期間において残っている許可されているリクエスト数 |
||||
* `X-Rate-Limit-Reset` - 許可されているリクエストの最大数にリセットされるまで待たなければならない秒数 |
||||
|
||||
これらのヘッダは、上記のコード例で示されているように、[[yii\filters\RateLimiter::enableRateLimitHeaders]] を false に設定することで無効にすることが出来ます。 |
Loading…
Reference in new issue