Zend Framework:テンプレートエンジンをSmartyにするのサンプルに、認証が必要なページを追加してみます。

Zend_Aclを使って、ページへのアクセスが可能かチェックします。
認証は、Zend_Authを使ってみます。

前提

今回のサンプルは、データベースを使用します。
データベースは、MySQL5以降です。
また、PDOを使用していますので、PDO_MYSQLがPHPに組み込まれている必要があります。

PHP5.2.3をソースからインストールする場合のconfigureの例です。
--with-pdo-mysqlを指定して、PDOを組み込んでいます。

# ./configure \\
--with-apxs2 \\
--with-mysql=/usr/mysql \\
--with-config-file-path=/etc/php \\
--enable-mbstring \\
--with-zlib \\
--with-gd \\
--with-libmbfl \\
--with-jpeg-dir=/usr/lib \\
--with-png-dir=/usr/lib \\
--with-dom \\
--enable-mbregex \\
--without-pear \\
--enable-gd-native-ttf \\
--enable-bcmath \\
--enable-exif \\
--with-pdo-mysql=/usr/mysql

アクセス制御

次のようなアクセス制御を行うようにします。

ログインしていないユーザーは、anonymousというグループに所属するものとします。
anonymousグループに所属するユーザーは、トップページのようなログインを必要としないページのみを表示できます。

memberというグループに所属するユーザーは、次のページを表示できます。
/index/member
また、anonymousグループがアクセスできるページも表示できます。

adminというグループに所属するユーザーは、次のページを表示できます。
/index/admin
また、memberグループがアクセスできるページも表示できます。

サンプルのアクセス制御のソース

アクセスできるかどうかのチェックを、アクションコントローラのpreDispatchメソッドで行うようにしてみます。

PHP:
  1. <?php
  2. abstract class Custom_Default_Controller_Action extends Tz_Controller_Action
  3. {
  4.  
  5.     protected $_userInfo = null;
  6.    
  7.     public function init()
  8.     {
  9.         parent::init();
  10.     }
  11.  
  12.     public function preDispatch()
  13.     {
  14.         parent::preDispatch();
  15.  
  16.         $this->_checkAcl();
  17.     }
  18.  
  19.     /**
  20.      * 要求されたアクションにアクセスできるかチェックする。
  21.      * アクセス許可されていない場合は、ログイン画面を表示する。
  22.      */
  23.     private function _checkAcl()
  24.     {
  25.         $registry = Zend_Registry::getInstance();
  26.         if (isset($registry['isDbError']) == false || $registry['isDbError'] == false) {
  27.             $auth = new Zend_Session_Namespace('auth');
  28.             if (!empty($auth->user_id)) {
  29.                 $this->_userInfo = $this->_getUserInfo($auth->user_id);
  30.             }
  31.         }
  32.         else {
  33.             // DB接続にエラーがあった場合は、認証情報をクリアーしておく
  34.             Zend_Session::namespaceUnset('auth');
  35.         }
  36.  
  37.         require_once 'Zend/Acl.php';
  38.         require_once 'Zend/Acl/Role.php';
  39.         require_once 'Zend/Acl/Resource.php';
  40.  
  41.         $acl = new Zend_Acl();
  42.         $acl->add(new Zend_Acl_Resource('index'));
  43.  
  44.         $acl->addRole(new Zend_Acl_Role('anonymous'));// anonymousロールは、ログインしていないユーザー
  45.         $acl->addRole(new Zend_Acl_Role('member'), 'anonymous');// memberロールは、ログインしたユーザー
  46.         $acl->addRole(new Zend_Acl_Role('admin')); // adminロールは、すべてにアクセス可能なユーザー
  47.        
  48.         // 誰でもアクセス可能なindexコントローラのアクションの設定
  49.         $acl->allow('anonymous', 'index', 'index');
  50.         $acl->allow('anonymous', 'index', 'throw');
  51.         $acl->allow('anonymous', 'index', 'test');
  52.  
  53.         // memberロールを持ったユーザーのみアクセス可能なindexコントローラのアクションの設定
  54.         $acl->allow('member', 'index', 'member');
  55.  
  56.         // Logon,Error,Sorryコントローラへは誰でもアクセス可能
  57.         $acl->add(new Zend_Acl_Resource('logon'));
  58.         $acl->allow(null, 'logon');
  59.         $acl->add(new Zend_Acl_Resource('error'));
  60.         $acl->allow(null, 'error');
  61.         $acl->add(new Zend_Acl_Resource('sorry'));
  62.         $acl->allow(null, 'sorry');
  63.  
  64.         // adminはすべてアクセス許可
  65.         $acl->allow('admin');
  66.  
  67.         $request = $this->getRequest();
  68.         $controller = $request->getControllerName();
  69.         $action = $request->getActionName();
  70.  
  71.         if (empty($this->_userInfo)) {
  72.             $role = 'anonymous';
  73.         }
  74.         else {
  75.             $role = $this->_userInfo->role;
  76.         }
  77.  
  78.         $is_allowed = false;
  79.         if ($acl->has($controller)) {
  80.             $is_allowed = $acl->isAllowed($role, $controller, $action);
  81.         }
  82.         else {
  83.             if ($role == 'admin') {
  84.                 $is_allowed = true;
  85.             }
  86.         }
  87.  
  88.         if (!$is_allowed) {
  89.             if (empty($auth->user_id)) {
  90.                 // Logonコントローラに現在のリクエストオブジェクトのコピーを渡すための処理。
  91.                 // (ログイン後、元のリクエストに対応するページを表示する情報となる)
  92.                 $front = $this->getFrontController();
  93.                 $dipatcher = $front->getDispatcher();
  94.                 $dipatcher->setParam('originalRequest',clone $request);
  95.                
  96.                 // ログイン画面を表示する
  97.                 $this->_forward('index','logon');
  98.             }
  99.             else {
  100.                 // アクセス権限がない旨を表示する画面に移動
  101.                 $this->_redirect('/sorry');
  102.             }
  103.         }
  104.     }
  105.  
  106.     /**
  107.      * ユーザーIDに対応するユーザー情報を取得する。
  108.      */
  109.     private function _getUserInfo($user_id)
  110.     {
  111.         $webapp_dir = $this->_registry['webappDir'];
  112.         require_once $webapp_dir . '/modules/default/models/table/Users.php';
  113.  
  114.         $table = new Table_Users();
  115.         $rowset = $table->find($user_id);
  116.         if ($rowset->count()) {
  117.             return $rowset->current();
  118.         }
  119.         return false;
  120.     }
  121. }

ログインしていないユーザーが、ログインが必要なページにアクセスしたとき、ログインページを表示するようにしています。

認証

Zend_Auth_Adapter_DbTableを使用して、認証を行ってみます。
データベース上のユーザー情報を格納したテーブルを参照することになります。

ユーザー情報テーブル

passwordカラムには、MD5でハッシュ化した値を格納します。

SQL:
  1. CREATE TABLE `td_users` (
  2.   `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  3.   `role` varchar(128) NOT NULL,
  4.   `username` varchar(128) NOT NULL,
  5.   `email` varchar(128) NOT NULL,
  6.   `password` varchar(128) NOT NULL,
  7.   `updatetime` datetime DEFAULT NULL,
  8.   `createtime` datetime DEFAULT NULL,
  9.   PRIMARY KEY  (`user_id`),
  10.   UNIQUE KEY `username` (`username`)
  11. ) ENGINE=MyISAM DEFAULT CHARSET=utf8

次のようなユーザー情報が格納されます。

SQL:
  1. +---------+--------+-----------+-------------------+----------------------------------+------------+------------+
  2. | user_id | role   | username  | email             | password                         | updatetime | createtime |
  3. +---------+--------+-----------+-------------------+----------------------------------+------------+------------+
  4. | 1       | admin  | adminuser | admin@localdomain | 25e4ee4e9229397b6b17776bfceaf8e7 |            |            |
  5. | 2       | member | testuser  | test@localdomain  | 179ad45c6ce2cb97cf1029e212046e81 |            |            |
  6. +---------+--------+-----------+-------------------+----------------------------------+------------+------------+

サンプルの認証のソース

PHP:
  1. <?php
  2.  
  3. class LogonController extends Custom_Default_Controller_Action
  4. {
  5.     const SESS_NAME_FORM_OPT = 'form_opt';
  6.  
  7.     private $_sessFormOpt = null;
  8.    
  9.     public function init()
  10.     {
  11.         parent::init();
  12.  
  13.         $this->_sessFormOpt = new Zend_Session_Namespace(self::SESS_NAME_FORM_OPT);
  14.  
  15.         // Logonコントローラに来る前にアクセ時のrequestオブジェクト
  16.         $original_request = $this->getInvokeArg('originalRequest');
  17.         if ($original_request) {
  18.             $this->view->assign('original_path',$original_request->getPathInfo());
  19.         }
  20.     }
  21.    
  22.     public function indexAction()
  23.     {
  24.         $sess_form_opt = new Zend_Session_Namespace('form_opt');
  25.         $sess_auth = new Zend_Session_Namespace('auth');
  26.  
  27.         $request = $this->getRequest();
  28.  
  29.         $username = $request->getPost('username');
  30.         if (empty($username)) {
  31.             if (isset($sess_auth->user_id)) {
  32.                 $this->_redirect('/');
  33.                 exit;
  34.             }
  35.             $this->_resetToken();
  36.             return;
  37.         }
  38.        
  39.         $token = $request->getPost('token');
  40.         if ($token != $sess_form_opt->token) {
  41.             $this->view->assign('result_msg','トークンのチェックに失敗しました。');
  42.            
  43.             $this->_resetToken();
  44.             return;
  45.         }
  46.  
  47.         $username = trim($request->getPost('username'));
  48.         $password = $request->getPost('password');
  49.  
  50.         // 入力値をチェック
  51.         require_once 'Zend/Validate/Alnum.php';
  52.         $v = new Zend_Validate_Alnum();
  53.         if (!$v->isValid($username)) {
  54.             $this->view->assign('result_msg','ユーザー名に英数字を入力してください。');
  55.             $this->_resetToken();
  56.             return;
  57.         }
  58.         $v = new Zend_Validate_Alnum();
  59.         if (!$v->isValid($password)) {
  60.             $this->view->assign('result_msg','パスワードに英数字を入力してください。');
  61.             $this->_resetToken();
  62.             return;
  63.         }
  64.  
  65.         require_once 'Zend/Auth/Result.php';
  66.         require_once 'Zend/Auth/Adapter/DbTable.php';
  67.  
  68.         $result = false;
  69.         try {
  70.             $zdb_adapter = Zend_Registry::get('zdb_adapter');
  71.  
  72.             $adapter = new Zend_Auth_Adapter_DbTable($zdb_adapter, 'td_users', 'username', 'password','MD5(?)');
  73.             $adapter->setIdentity($username);
  74.             $adapter->setCredential($password);
  75.  
  76.             $result = $adapter->authenticate();
  77.         }
  78.         catch (Exception $exception) {
  79.             $this->view->assign('result_msg','エラーです。' . $exception->getMessage());
  80.             $this->_resetToken();
  81.             return;
  82.         }
  83.  
  84.         if ($result && $result->isValid()) {
  85.             $id = $result->getIdentity();
  86.             $row_obj = $adapter->getResultRowObject('user_id');
  87.  
  88.             $sess_auth->user_id = $row_obj->user_id;
  89.  
  90.             $original_path = $request->getPost('original_path');
  91.             if (isset($original_path)) {
  92.                 require_once 'Zend/Validate/Regex.php';
  93.                 $v = new Zend_Validate_Regex('/^\/[a-zA-Z0-9\/]*$/');
  94.                 if ($v->isValid($original_path)) {
  95.                     $this->_redirect($original_path);
  96.                     exit;
  97.                 }
  98.             }
  99.             $this->_redirect('/');
  100.         }
  101.         else {
  102.             $this->_resetToken();
  103.         }
  104.     }
  105.  
  106.     public function logoffAction()
  107.     {
  108.         Zend_Session::namespaceUnset('auth');
  109.         $this->_redirect('/');
  110.     }
  111.  
  112.     private function _resetToken()
  113.     {
  114.         $sess_form_opt = $this->_sessFormOpt;
  115.        
  116.         $token = md5(mt_rand());
  117.         $this->view->assign('token',$token);
  118.         $sess_form_opt->token = $token;
  119.     }
  120. }

サンプルダウンロード

ダウンロードページにアーカイブしたファイルを置きました。

データベースへのデータのインポート

データベースは、次のようなSQLで作成します。
mysql> CREATE DATABASE td06db CHARACTER SET utf8;

ユーザー名:tdtestuser、パスワード:tdtestuserで、td06dbデータベースにアクセスできるようにします。
mysql> GRANT ALL ON td06db.* to tdtestuser@localhost IDENTIFIED BY 'tdtestuser';

データは、次のようにtd06dbデータベースにインポートします(※)。

$ cd tdBASEDIR06/webapp06/sql
$ mysql -u tdtestuser -p td06db < td06db.sql

※ td_usersテーブルを作成して、そのテーブルにデータを挿入します。

Tags: , , , ,

コメントをどうぞ