0 follower

ユーザ認証

このブログアプリケーションでは、システムオーナとゲストユーザの区別が必要です。 したがって、ユーザ認証 機能を実装する必要があります。

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

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

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

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

<?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 を継承しているため、 アクティブレコードの機能 を利用して、 オブジェクト指向(OOP)の流儀で tbl_user テーブルにアクセスすることが出来ます。

ユーザが正当なパスワードを入力したかどうかをチェックするため、User クラスの validatePassword メソッドを呼び出しています。 /wwwroot/blog/protected/models/User.php を以下の様に修正する必要があります。 平文のパスワードをデータベースに保存するのではなく、パスワードのハッシュを保存することに注意してください。 ユーザが入力したパスワードを検証する際は、パスワードではなくハッシュの結果を比較しなければなりません。 パスワードのハッシュと検証には、Yii に組み込まれている CPasswordHelper クラスを使います。

class User extends CActiveRecord
{
    ......
    public function validatePassword($password)
    {
        return CPasswordHelper::verifyPassword($password,$this->password);
    }
 
    public function hashPassword($password)
    {
        return CPasswordHelper::hashPassword($password);
    }
}

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

ヒント: UserIdentity クラスにおいては、対応するクラスファイルを明示的に読み込むことなく CUserIdentity を参照しています。 これは CUserIdentity が Yii framework のコアクラスであるためです。Yii は任意のコアクラスが最初に参照されたときに、自動的にそのクラスファイルを読み込みます。

User クラスでも同じことが行われています。 なぜなら、User クラスファイルが /wwwroot/blog/protected/models ディレクトリ以下にあり、そのディレクトリがアプリケーション初期構成の下記コードで PHP の include_path に追加されているからです。

return array(
    ......
    'import'=>array(
        'application.models.*',
        'application.components.*',
    ),
    ......
);

上記の初期構成は /wwwroot/blog/protected/models/wwwroot/blog/protected/components の下にクラスファイルがある全てのクラスは、最初にクラスが参照された時点で自動的に読み込まれることを示します。

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

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

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

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

Found a typo or you think this page needs improvement?
Edit it on github !