ユーザ認証
===================

このブログアプリケーションでは、システムオーナとゲストユーザの区別が必要です。 したがって、[ユーザ認証](http://www.yiiframework.com/doc/guide/ja/topics.auth) 機能を実装する必要があります。

気付いていらっしゃるかも知れませんが、スケルトンアプリケーションにはすでにユーザ認証機能が備わっています。
これは、ユーザ名とパスワードのどちらもが `demo` もしくは `admin` であればユーザとして認証する、というものです。
このセクションでは、ユーザ認証を `User` データベーステーブルに基づいて行うように、対応するコードを修正します。

ユーザ認証は [IUserIdentity] インタフェースを実装するクラスで行われます。スケルトンアプリケーションでは、この目的のために `UserIdentity` クラスを使っています。このクラスファイルは、`/wwwroot/blog/protected/components/UserIdentity.php` に保存されています。

> Tip|ヒント: 規約により、クラスファイル名は対応するクラス名に接尾辞として拡張子 `.php` を付けたものでなければなりません。この規約に従えば、[パスエイリアス](http://www.yiiframework.com/doc/guide/ja/basics.namespace) を使ってクラスを参照することが可能になります。 例えば、`UserIdentity` クラスを、`application.components.UserIdentity` というエイリアスで参照することができます。Yii では多くの API がパスエイリアスを認識します(例えば [Yii::createComponent()|YiiBase::createComponent])。そして、パスエイリアスを使えば、コードに絶対パスを埋め込む必要がなくなります。コードに埋め込まれた絶対パスは、しばしばアプリケーション配備の際にトラブルを引き起こします。

`UserIdentity` クラスを以下のように修正します。

~~~
[php]
<?php
class UserIdentity extends CUserIdentity
{
	private $_id;

	public function authenticate()
	{
		$username=strtolower($this->username);
		$user=User::model()->find('LOWER(username)=?',array($username));
		if($user===null)
			$this->errorCode=self::ERROR_USERNAME_INVALID;
		else if(!$user->validatePassword($this->password))
			$this->errorCode=self::ERROR_PASSWORD_INVALID;
		else
		{
			$this->_id=$user->id;
			$this->username=$user->username;
			$this->errorCode=self::ERROR_NONE;
		}
		return $this->errorCode==self::ERROR_NONE;
	}

	public function getId()
	{
		return $this->_id;
	}
}
~~~

この `authenticate()` メソッドにおいては、`User` クラスを用いて、`tbl_user` テーブルの中から、`username` カラムが与えられた `username` と同じである行を探し出しています(大文字と小文字は区別しません)。`User` クラスは前のセクションで `gii` ツールによって作られたものであることを思い出してください。`User` クラスは [CActiveRecord] を継承しているため、 [アクティブレコードの機能](http://www.yiiframework.com/doc/guide/ja/database.ar) を利用して、オブジェクト指向(OOP)の流儀で `tbl_user` テーブルにアクセスすることが出来ます。

ユーザが正当なパスワードを入力したかどうかをチェックするため、`User` クラスの `validatePassword` メソッドを呼び出しています。
`/wwwroot/blog/protected/models/User.php` を以下の様に修正する必要があります。
平文のパスワードをデータベースに保存するのではなく、パスワードとランダムに発生させたソルトキーのハッシュを保存することに注意してください。
ユーザが入力したパスワードを検証する際は、パスワードではなくハッシュの結果を比較することになります。
パスワードのハッシュと検証には、PHP の組み込み関数である `crypt()` を使います。
詳細な解説としては、Wiki 記事 [Use crypt() for password storage](http://www.yiiframework.com/wiki/425) を参照して下さい。
 

~~~
[php]
class User extends CActiveRecord
{
	......
	public function validatePassword($password)
	{
		return crypt($password,$this->password)===$this->password;
	}

	public function hashPassword($password)
	{
		return crypt($password, $this->generateSalt());
	}
}
~~~

`UserIdentity` クラスでは、`getId()` メソッドをオーバーライドして、`tbl_user` テーブルから見つかったユーザの `id` を返すようにしています。元の実装では、代わりにユーザ名を返すようになっていました。`username` と `id` プロパティはともにユーザセッションに保存され、コードのどこからでも `Yii::app()->user` でアクセスすることが可能です。

> Tip|ヒント: `UserIdentity` クラスにおいては、対応するクラスファイルを明示的に読み込むことなく [CUserIdentity] を参照しています。 これは [CUserIdentity] が Yii framework のコアクラスであるためです。Yii は任意のコアクラスが最初に参照されたときに、自動的にそのクラスファイルを読み込みます。
>
>`User` クラスでも同じことが行われています。 なぜなら、`User` クラスファイルが `/wwwroot/blog/protected/models` ディレクトリ以下にあり、そのディレクトリがアプリケーション初期構成の下記コードで PHP の `include_path` に追加されているからです。
>
> ~~~
> [php]
> return array(
>     ......
>     'import'=>array(
>         'application.models.*',
>         'application.components.*',
>     ),
>     ......
> );
> ~~~
>
> 上記の初期構成は `/wwwroot/blog/protected/models` か `/wwwroot/blog/protected/components` の下にクラスファイルがある全てのクラスは、最初にクラスが参照された時点で自動的に読み込まれることを示します。

`UserIdentity` クラスは主に `LoginForm` クラスで、ログインページで入力されたユーザ名とパスワードを元にユーザを認証するために使われます。以下のコード断片で、`UserIdentity` がどのように使われるのかを示します。

~~~
[php]
$identity=new UserIdentity($username,$password);
$identity->authenticate();
switch($identity->errorCode)
{
	case UserIdentity::ERROR_NONE:
		Yii::app()->user->login($identity);
		break;
	......
}
~~~

> Info|情報: identity クラスと `user` アプリケーションコンポーネントはしばしば混同されます。前者は認証を行う方法のことであり、後者は現在のユーザに関する情報をあらわします。アプリケーションが持てる `user` コンポーネントは一つだけですが、identity クラスは、どのような認証方法をサポートするかによって、一つまたは複数のクラスを持つことができます。いったん認証が成功すれば、identity インスタンスから `user` コンポーネントへ認証の情報が渡され、アプリケーション全体から `user` を用いてアクセス可能になります。

修正後の `UserIdentity` クラスをテストするために、ブラウザで URL `http://www.example.com/blog/index.php` にアクセスして、`tbl_user` テーブルに保存したユーザ名とパスワードでログインを試みてください。[ブログデモ](http://www.yiiframework.com/demos/blog/) で提供されるデータベースを利用している場合は、ユーザ名 `demo`、パスワード `demo` でアクセスできるはずです。このブログシステムにはユーザ管理機能が無いことに注意して下さい。結果として、ユーザはウェブインタフェースを通じて自身のアカウントを変更したり、新しいアカウントを作成したりは出来ません。ユーザ管理機能はブログアプリケーションの将来の機能拡張であると見なして良いでしょう。

<div class="revision">$Id$</div>
