Yiiでのユーザ認証まわりを修正します。
したいことは
- パスワードについてはphpassを利用して暗号化したい。
- ログインにはメルアドとパスワードを利用したい。
- アクセスコントロールは管理者、マネージャー、一般ユーザのように簡単にしたい。
となります。
パスワードについてはphpassを利用して暗号化したい。
まず、ここからphpassの最新版をダウンロードし、protected/extensions/passwordHash/PasswordHash.phpとして保存します。
次にmain.phpにphpassの設定を追記します。
'import'=>array( // for phpass 'application.extensions.passwordHash.PasswordHash', ), 'params'=>array( //for phpass 'phpass'=>array( 'iteration_count_log2' => 8, 'portable_hashes' => false, ), ),
なお、iteration_count_log2 はストレッチングに利用する数値です。例えば8の場合、2^8=256回、暗号化アルゴリズムが適用されます。この数値は4から31の間で設定します。
portable_hashesをtrueに設定すると、暗号化したパスワードにsaltを含むことになり、あまり推奨されません。phpのバージョンが5.3以上ならfalseにしたほうがよいです。
認証の処理を修正する必要があるので、protected/components/以下にUserIdentity.phpを作成し、以下のような内容を記述します。
/** * UserIdentity represents the data needed to identity a user. * It contains the authentication method that checks if the provided * data can identity the user. */ class UserIdentity extends CUserIdentity { private $_id; /** * Authenticates a user. * The example implementation makes sure if the username and password * are both 'demo'. * In practical applications, this should be changed to authenticate * against some persistent user identity storage (e.g. database). * @return boolean whether authentication succeeds. */ public function authenticate() { $user=User::model()->findByAttributes(array('username'=>$this->username)); $ph = new PasswordHash( Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes'] ); if($user==null) $this->errorCode=self::ERROR_USERNAME_INVALID; else if(!$ph->CheckPassword($this->password, $user->password)) $this->errorCode=self::ERROR_PASSWORD_INVALID; else { $this->_id=$user->id; $this->errorCode=self::ERROR_NONE; } return $this->errorCode==self::ERROR_NONE; } public function getId() { return $this->_id; } }
これで、認証の際に、入力されたパスワードをphpassで暗号化し、DBに保存されているパスワードと一致しているかチェックするようになります。
最後にUserテーブルへのデータ登録時に、パスワードをphpassで暗号化する部分を用意します。修正するファイルはprotected/models/User.phpです。
class User extends CActiveRecord { protected function beforeSave() { if(!empty($this->password)) { $ph = new PasswordHash( Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes'] ); $this->password = $ph->HashPassword($this->password); } return parent::beforeSave(); } }
ログインにはメルアドとパスワードを利用したい。
作業としては、Userテーブルにemailフィールドを追加し、Userモデルにも必要な設定を行った後、ログインページと認証部分の修正となります。
ログインページの修正については、protected/views/site/login.phpを以下のような感じに修正します。なお、Yii-bootstarpを利用しているので、widgetはbootstarpのものを利用しています。Yii-bootstrapの詳しい書式については、こちらを参考にしてください。
<?php $this->pageTitle=Yii::app()->name . ' - ログイン'; $this->breadcrumbs=array( 'ログイン', ); ?> <h1>ログイン</h1> 登録されたメールアドレスとパスワードを利用してログインしてください。<br /> <div class="form"> <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array( 'id'=>'login-form', 'focus'=>array($model,'email'), )); ?> <p class="help-block">「 <span class="required">*</span> 」のある欄は入力必須です。</p> <?php echo $form->errorSummary($model); ?> <?php echo $form->textFieldRow($model,'email',array('class'=>'span5','maxlength'=>128)); ?> <?php echo $form->passwordFieldRow($model,'password',array('class'=>'span5','maxlength'=>128)); ?> <?php echo $form->checkBoxRow($model,'rememberMe'); ?> <div class="form-actions"> <?php $this->widget('bootstrap.widgets.TbButton', array( 'buttonType'=>'submit', 'type'=>'primary', 'label'=>'ログイン', )); ?> </div> <?php $this->endWidget(); ?> </div><!-- form -->
認証部分は、先に出てきたprotected/components/UserIdentity.phpを修正します。
/** * UserIdentity represents the data needed to identity a user. * It contains the authentication method that checks if the provided * data can identity the user. */ class UserIdentity extends CUserIdentity { const ERROR_EMAIL_INVALID=3; private $_id; public $email; public function __construct($email,$password) { $this->email=$email; $this->password=$password; } /** * Authenticates a user. * The example implementation makes sure if the username and password * are both 'demo'. * In practical applications, this should be changed to authenticate * against some persistent user identity storage (e.g. database). * @return boolean whether authentication succeeds. */ public function authenticate() { $email=strtolower($this->email); $user=User::model()->find('email=?',array($email)); $ph = new PasswordHash( Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes'] ); if($user==null) $this->errorCode=self::ERROR_EMAIL_INVALID; else if(!$ph->CheckPassword($this->password, $user->password)) $this->errorCode=self::ERROR_PASSWORD_INVALID; else { $this->_id=$user->id; $this->errorCode=self::ERROR_NONE; } return $this->errorCode==self::ERROR_NONE; } public function getId() { return $this->_id; } }
アクセスコントロールは管理者、マネージャー、一般ユーザのように簡単にしたい。
作業としては、Userテーブルにroleフィールドを用意したあと、Userモデルや認証部分、表示部分などの修正となります。
ロールについては、管理者(3)、マネージャー(2)、一般ユーザ(1)とします。
Userモデルでは、以下のような修正をします。
class User extends CActiveRecord { const USER_ROLE_ADMIN=3; const USER_ROLE_MANAGER=2; const USER_ROLE_USER=1; }
認証部分は、先に出てきたprotected/components/UserIdentity.phpを修正します。
/** * UserIdentity represents the data needed to identity a user. * It contains the authentication method that checks if the provided * data can identity the user. */ class UserIdentity extends CUserIdentity { const ERROR_EMAIL_INVALID=3; private $_id; public $email; public function __construct($email,$password) { $this->email=$email; $this->password=$password; } /** * Authenticates a user. * The example implementation makes sure if the username and password * are both 'demo'. * In practical applications, this should be changed to authenticate * against some persistent user identity storage (e.g. database). * @return boolean whether authentication succeeds. */ public function authenticate() { $email=strtolower($this->email); $user=User::model()->find('email=?',array($email)); $ph = new PasswordHash( Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes'] ); if($user==null) $this->errorCode=self::ERROR_EMAIL_INVALID; else if(!$ph->CheckPassword($this->password, $user->password)) $this->errorCode=self::ERROR_PASSWORD_INVALID; else { $this->_id=$user->id; $this->setState('role', $user->role); $this->errorCode=self::ERROR_NONE; } return $this->errorCode==self::ERROR_NONE; } public function getId() { return $this->_id; } }
追加したのは
$this->setState('role', $user->role);
の部分です。この処理でセッションにroleの内容を書き出し、必要な箇所で利用できるようにします。なお、セッションからの取り出しには、
Yii::app()->user->getState('roles');
か
Yii::app()->user->roles
を利用します。
アクセスコントロール部分をprotected/components/以下にWebUser.phpとして新規に作成します。
class WebUser extends CWebUser { /** * Overrides a Yii method that is used for roles in controllers (accessRules). * * @param string $operation Name of the operation required (here, a role). * @param integer $user_id (opt) * @return bool Permission granted? */ public function checkAccess($operation, $user_id=null) { if (empty($this->id)) { // Not identified => no rights return false; } $role = $this->getState("role"); if ($role == User::USER_ROLE_ADMIN) { return true; // admin role has access to everything } if (strstr($operation,$role) !== false) { // Check if multiple roles are available if(empty($user_id)) return true; else return ($this->id==$user_id); } // allow access if the operation request is the current user's role return ($operation == $role); } }
また、この処理を利用できるようにprotected/config/main.phpに追記しておきます。
'components'=>array( 'user'=>array( 'class' => 'WebUser', ), ),
コントローラのアクションでアクセス制限をかける場合は、以下のようにします。
/** * @return array action filters */ public function filters() { return array( 'accessControl', // perform access control for CRUD operations ); } /** * Specifies the access control rules. * This method is used by the 'accessControl' filter. * @return array access control rules */ public function accessRules() { return array( array('allow', // allow all users 'actions'=>array('view'), 'users'=>array('*'), ), array('allow', // allow authenticated user 'actions'=>array('create','update','delete'), 'users'=>array('@'), ), array('allow', // allow admin user 'actions'=>array('admin'), 'roles'=>array(User::USER_ROLE_ADMIN), ), array('deny', // deny all users 'users'=>array('*'), ), ); }
上記の場合、adminアクションはroleがUser::USER_ROLE_ADMINの人、つまり管理者しかアクセスできないとなります。
ビューで表示をコントロールする場合は、以下のような感じに書きます。
<?php if(Yii::app()->user->checkAccess(User::USER_ROLE_ADMIN, User::USER_ROLE_MANAGER)) :?> 管理者とマネージャー向けの表示 <?php else: ?> 管理者とマネージャー以外の方向けの表示 <?php endif; ?>
CMenu Widgetなどで利用する場合は、以下のような感じに書きます。
<?php $this->widget('zii.widgets.CMenu',array( 'items'=>array( array('label'=>'Admin', 'url'=>array('/manageUser/admin'), 'visible'=>Yii::app()->user->checkAccess(User::USER_ROLE_ADMIN)), array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest), array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest) ), )); ?>
1件のコメント
コメントは受け付けていません。