この章から読み始める方へ
- 前章までの成果物はこちらからダウンロードしていただけます。
- zipをhtdocs以下にそのまま展開してください
- データベースのuser.sqlをphpmyadminなどでyiiという空のデータベース下にインポートしてください。
- MySQLのrootのパスワードはunko9314です。
- もし上記の設定と異なる場合はxampp\htdocs\protected\config\main.phpを参照し、該当箇所を変更してください
この章のテーマ
交流サイトなどでは会員ごとに異なったステータスが割り当てられており、それによってアクセスできるページに制限を設けます。例えばログインしているユーザには一般会員とプレミアム会員が割り当てられていて、特定のページはプレミアム会員しかアクセスできないといったものです。
この章では、とりあえずユーザを管理ユーザと一般ユーザに分けます。ユーザのステータス(すなわち管理ユーザか一般ユーザかという情報)に基づいて特定のControllerのActionを実行できたり、できなかったりという設定を行う方法を見ていきます。
もちろん各actionのコードの中に直接、管理ユーザか一般ユーザを判定するプログラムを書いて処理をしてもよいのですが、それだと同じコードを何箇所も書く必要があるので、あまり美しいやり方とは言えません。yiiのControllerには元々、アクセス制限の機能が備わっていて、それを拡張することにします。
データベースとModelの変更
ALTER TABLE user ADD role tinyint(1) NOT NULL DEFAULT 0;
http://localhost/phpmyadmin にログインしyiiのデータベースを選択し上記のSQLを実行します。(図を参照)
これでuserのデータベースにroleという項目が追加されました。roleは数値データで、ユーザが一般ユーザか管理ユーザかを表す値だとします。
本ブログの説明ではrole値が0が一般ユーザで、1を特権ユーザとします。
- 0: 一般ユーザ
- 1: 管理ユーザ
さて次にModelの変更をしていきましょう。protected/models/User.phpを開きましょう。
以下のようにroleの検証ルールを追加します。Modelのrulesメソッドでは入力されたデータが設定したルールに違反していなかをチェックします。ここでは入力された値が0または1でなければならないという意味を表しています。
public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('email, name', 'required'), array('email', 'length', 'max'=>128), array('name', 'length', 'max'=>64), array('password','length','max'=>64,'min'=>4), // passwordを追加 array('role','in','range'=>array(0,1)), // roleを追加 // The following rule is used by search(). // @todo Please remove those attributes that should not be searched. array('id, email, name', 'safe', 'on'=>'search'), ); }
次に要素名(ラベル名)の定義も追加しておきます。
/** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'id' => 'ID', 'email' => 'Email', 'name' => 'Name', 'password'=> 'Password', // passwordを追加 'role' => 'Role', // roleを追加 ); }
データベースとModelの部分に関する変更は以上です。
CWebUserの拡張
次にCWebUserと呼ばれるクラスを拡張していきます。
前にE-mailでログインできるように改造しましたが、その時にコードの中に
Yii::app()->user->id;
というものが出てきたことを覚えているかもしれません。このYii::app()->userの定義の部分を拡張していきます。ここを拡張しておくことで、のちにControllerのアクセス制限の部分を思った通りの処理にしやすいからです。
(1) まずprotected/config/main.phpを開きます。
// application components 'components'=>array( 'user'=>array( // 自分で定義したクラスに変更する 'class' => 'WebUser', // protected/components/WebUser.php // enable cookie-based authentication 'allowAutoLogin'=>true, ),
上記のように「‘class’ => ‘WebUser’,」 という記述を追加します。これはYii::app()->userを自分で定義したクラス(WebUser)に変更するようにします。
それによりYii::app()->userに対して自分で定義したメソッドを追加できるようになります。例えばYii::app()->user->isAdmin()のように自分が管理者身分であるかどうかを返すメソッドを追加すること等ができます。それを追加しておくことで、Controllerのアクセス制限の処理が分かりやすく記述できるようになります。
(2) 次にmain.php内に記述したWebUserというクラスを自分で定義します。
具体的には、protected/componentsの中にWebUser.phpというファイルを追加します。そして以下のように記述してください。
<?php class WebUser extends CWebUser { // 通常のユーザかどうか public function getIsUser() { // ログインしていない場合 if($this->isGuest) return false; // 自分のUserデータを取得 $user = User::model()->findByPk($this->id); // 空の場合 if(!isset($user)) return false; // ユーザかどうか if($user->role == 0) return true; } // 管理者かどうか public function getIsAdmin() { // ログインしていない場合 if($this->isGuest) return false; // 自分のUserデータを取得 $user = User::model()->findByPk($this->id); // 空の場合 if(!isset($user)) return false; // ユーザかどうか if($user->role == 1) return true; } } ?>
getIsUser()やgetIsAdmin()のメソッドでは、まずはじめにログインしていることを確認して、ログイン後にセッション上に保存されているidからデータベースに問い合わせをして、Userクラスのカラムのroleの値を見て0だったら一般ユーザ、1だったら管理ユーザというような判定をしています。
そこで鋭い人なら以下の疑問が浮かんでいるかもしれません。
そういえば、前にYii::app()->user->idに格納される値を通し番号(Userデータベースのid)にかわるように処理したけどそれって、components/UserIdentity.phpに記述したのじゃなかったっけ??
はい、そのとおりです。これと同じ質問をしているのがこちらのページにあります。結論から言いますと、Ypp::app()->user自体はCWebUser(WebUser)のクラスです。おおもとを辿って、framework/web/auth/CWebUser.phpのlogin()メソッドを見れば分かるかと思いますが、ここでUserIdentityの引数を受け取って中のidをコピーしています。
ですんでYpp::app()->userはあくまでWebUserのクラスだと思っておいてください。
Controllerにアクセス制限を加えよう
これからいよいよこの章の肝に入っていきます。前に作成したprotected/controllers/UserController.phpを開きましょう。
このコントローラはログイン済の一般ユーザを対象にしたページを表示するためのクラスだとします。ですので、ログインしていないユーザや、管理ユーザはこのページには入れないようにします。
// アクセス制限を利用する public function filters() { return array('accessControl',); } // アクセスルール public function accessRules() { return array( // 一般ユーザに対して以下に列挙するactionの実行を許可する array('allow', 'actions'=>array('index','test'), 'expression'=>'$user->isUser', ), // 上記のルールに適合しないすべてのユーザを排除 array('deny', 'users'=>array('*'), // *: 全ユーザ ?: 匿名ユーザ @: 認証済みのユーザ ), ); }
そして以上のアクセス制限のルールを記述したメソッドを追加します。さて、いますでに「index」「test」の2つのアクションを作ってあるかと思います。さきほど定義したYii::app()->user->isUserの部分がtrueになったら、indexとtestのそれぞれのアクションの実行を許可するという意味のルールになっています。またそのルールに適合しないすべてのユーザを排除するようなルールがその後の部分です。
正しく動作することを確かめよう
さて上記が正しく動作することを確かめてみましょう。
- E-mail yii@juncheng.org
- パスワード test
でログインします。いまこのユーザのroleの値は0になっています。ですのでログイン後、http://localhost/user/test/にアクセスすると正しく表示されます。
さてhttp://localhost/phpmyadminから、このユーザのroleの値を1に変更してみましょう。phpmyadminからデータベースの値をいじるのは以前の章でもやったのでここでは再び説明しません。
そして再びログインしてから、http://localhost/user/testにアクセスしてみます。すると・・・・
以上のように403というエラーが出てアクセス権がない旨が表示されました。これでアクセス制限がうまくいっていることがわかりました。
さて、ついでですが表示言語が英語になっているので、このタイミングですが日本語にしておきましょう。
protected/config/main.phpを開いてください。そして、以下のように言語設定を追加します。
<?php // uncomment the following to define a path alias // Yii::setPathOfAlias('local','path/to/local-folder'); // This is the main Web application configuration. Any writable // CWebApplication properties can be configured here. return array( 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name'=>'My Web Application', // 表示言語を変更 'language'=>'ja', // ここを追加した
そうすると先ほどのエラー画面が次のようになります。
ログイン後に自動でページ遷移するようにする
さて会員専用ページを持つウェブサイトなどでは基本的にログインしたら、そのまま直後に会員制ページに飛ぶようになっているので、このシステムでもログイン後に自動的に会員ページに遷移するように変更を加えます。
protected/controllers/SiteController.phpを開きます。だいたい94行目のところを以下のように変更してください。actionLoginのメソッドの中なのですが、ログインに成功したあと一般ユーザであれば、http://localhost/userに遷移するようにしました。
if($model->validate() && $model->login()) { // ログイン後に自動でページ遷移するように変更 if(Yii::app()->user->isUser) { // 一般ユーザであれば/userに遷移 $this->redirect('/user'); } else { $this->redirect(Yii::app()->user->returnUrl); } }
さて以上で今回の解説は終わります。 今回の成果物をまたこちらに置いておきます。必要に応じて使ってください。
参考文献
以下のページを参考にさせていただきました。
http://qiita.com/tanakahisateru/items/6759ed45195026400694