Auth/ACL 快速入門

edited 十月 2013 in CakePHP
這是在 PHP Framework 讀書會( http://groups.google.com/group/zf-tw/ ) 12/17 所分享的內容,內容參考了官方文件 ( http://book.cakephp.org/view/641/Simple-Acl-controlled-Application ),沒有放太多說明在裡面,有人問再說吧 :)


1.加入會員與群組資料表
CREATE TABLE `members` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) collate utf8_unicode_ci NOT NULL,
  `password` varchar(128) collate utf8_unicode_ci NOT NULL,
  `group_id` int(11) NOT NULL,
  `user_status` char(1) collate utf8_unicode_ci NOT NULL default 'Y',
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `members_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `groups` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) collate utf8_unicode_ci NOT NULL,
  `parent_id` int(11) default NULL,
  PRIMARY KEY  (`id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

2.將 config/sql/db_acl.sql 匯入資料庫
3.修改 app_controller.php ,變成這樣
<?php
class AppController extends Controller {
    var $components = array('Acl');
}
4.透過 Bake 產生基本的資料存取程式(MVC)
5.修改 models/group.php ,變成這樣
<?php
class Group extends AppModel {
	var $name = 'Group';
	var $actsAs = array('Acl' => 'requester');

	function parentNode(){
	    if (!$this->id || !$parentId = $this->field('parent_id')) {
	        return null;
	    } else {
	        return array('model' => 'Group', 'foreign_key' => $parentId);
	    }
	}
}
6.修改 models/member.php ,變成這樣
<?php
class Member extends AppModel {
	var $name = 'Member';
	var $actsAs = array('Acl' => 'requester');
	var $belongsTo = array('Group');

	function parentNode(){
        if (!$this->id || !$groupId = $this->field('group_id')) {
          return null;
        } else {
          return array('model' => 'Group', 'foreign_key' => $groupId);
        }
    }
}
7.透過產生的介面新增一個群組(group)與一個會員(member),記得先新增群組,接著新增會員時才能夠指定會員屬於該群組,最後記住群組的 id
8.參考官方手冊中的說明,在 controllers/groups_controller.php 加入下面兩個方法
/**
	 * Rebuild the Acl based on the current controllers in the application
	 *
	 * @link          http://book.cakephp.org/view/647/An-Automated-tool-for-creating-ACOs
	 * @return void
	 */
	function buildAcl() {
	    $log = array();

	    $aco =& $this->Acl->Aco;
	    $root = $aco->node('controllers');
	    if (!$root) {
	        $aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
	        $root = $aco->save();
	        $root['Aco']['id'] = $aco->id;
	        $log[] = 'Created Aco node for controllers';
	    } else {
	        $root = $root[0];
	    }

	    App::import('Core', 'File');
	    $Controllers = Configure::listObjects('controller');
	    $Plugins = $this->_get_plugin_controller_names();
	    $Controllers = array_merge($Controllers, $Plugins);
	    $appIndex = array_search('App', $Controllers);
	    if ($appIndex !== false ) {
	        unset($Controllers[$appIndex]);
	    }
	    $baseMethods = get_class_methods('Controller');
	    $baseMethods[] = 'buildAcl';

	    // look at each controller in app/controllers
	    foreach ($Controllers as $ctrlName) {
	        App::import('Controller', $ctrlName);
	        $ctrlclass = $ctrlName . 'Controller';
	        $methods = get_class_methods($ctrlclass);

	        // find / make controller node
	        $controllerNode = $aco->node('controllers/'.$ctrlName);
	        if (!$controllerNode) {
	            $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
	            $controllerNode = $aco->save();
	            $controllerNode['Aco']['id'] = $aco->id;
	            $log[] = 'Created Aco node for '.$ctrlName;
	        } else {
	            $controllerNode = $controllerNode[0];
	        }

	        //clean the methods. to remove those in Controller and private actions.
	        foreach ($methods as $k => $method) {
	            if (strpos($method, '_', 0) === 0) {
	                unset($methods[$k]);
	                continue;
	            }
	            if (in_array($method, $baseMethods)) {
	                unset($methods[$k]);
	                continue;
	            }
	            $methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);
	            if (!$methodNode) {
	                $aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
	                $methodNode = $aco->save();
	                $log[] = 'Created Aco node for '. $method;
	            }
	        }
	    }
	    debug($log);
	}

	/**
     * Get the names of the plugin controllers ...
     *
     * This function will get an array of the plugin controller names, and
     * also makes sure the controllers are available for us to get the
     * method names by doing an App::import for each plugin controller.
     *
     * @link          http://book.cakephp.org/view/647/An-Automated-tool-for-creating-ACOs
     * @return array of plugin names.
     *
     */
	function _get_plugin_controller_names(){
	    App::import('Core', 'File', 'Folder');
	    $paths = Configure::getInstance();
	    $folder =& new Folder();
	    // Change directory to the plugins
	    $folder->cd(APP.'plugins');
	    // Get a list of the files that have a file name that ends
	    // with controller.php
	    $files = $folder->findRecursive('.*_controller\.php');
	    // Get the list of plugins
	    $Plugins = Configure::listObjects('plugin');

	    // Loop through the controllers we found int the plugins directory
	    foreach($files as $f => $fileName)
	    {
	        // Get the base file name
	        $file = basename($fileName);

	        // Get the controller name
	        $file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));

	        // Loop through the plugins
	        foreach($Plugins as $pluginName){
	            if (preg_match('/^'.$pluginName.'/', $file)){
	                // First get rid of the App controller for the plugin
	                // We do this because the app controller is never called
	                // directly ...
	                if (preg_match('/^'.$pluginName.'App/', $file)){
	                    unset($files[$f]);
	                } else {
	                    if (!App::import('Controller', $pluginName.'.'.$file))
	                    {
	                        debug('Error importing '.$file.' for plugin '.$pluginName);
	                    }

	                    /// Now prepend the Plugin name ...
	                    // This is required to allow us to fetch the method names.
	                    $files[$f] = $file;
	                }
	                break;
	            }
	        }
	    }

	    return $files;
	}
9.同樣在 controllers/groups_controller.php 中找到 function admin_index() ,改成這樣
function admin_index() {
	    $this->buildAcl();
	    $group =& $this->Group;
	    $group->id = 1;
	    $this->Acl->allow($group, 'controllers');
	    $this->Group->recursive = 0;
	    $this->set('groups', $this->paginate());
	}
*這裡的 $group->id 數值需要參考第 7 步所新增的資料
10.透過瀏覽器打開 http://localhost/xxx/admin/groups/
11.完成後把 controllers/groups_controller.php 的 function admin_index() 改回原本樣子
function admin_index() {
	    $this->Group->recursive = 0;
	    $this->set('groups', $this->paginate());
	}
12.接著修改 app_controller.php ,變成這樣
<?php
class AppController extends Controller {
    var $components = array('Acl', 'Auth');

    function beforeFilter() {
        if (isset($this->Auth)) {
            $this->Auth->userModel = 'Member';
            $this->Auth->userScope = array('Member.user_status' => 'Y');
            $this->Auth->loginAction = '/members/login';
            $this->Auth->authorize = 'actions';
        }
    }
}
13.修改 controllers/members_controller.php ,在 function index() 上方加入下面3個方法
function beforeFilter() {
	    parent::beforeFilter();
	    $this->Auth->allowedActions = array('login', 'logout');
	}

	function login() {

	}

	function logout() {
	    $this->Auth->logout();
	    $this->redirect(array('action' => 'login'));
	}
14.新增 views/members/login.ctp ,內容如下
<?php
echo $form->create('Member', array('action' => 'login'));
echo $form->input('username');
echo $form->input('password');
echo $form->end('Login');

完成了上面動作後,就可以透過在操作過程中新增的帳號、密碼登入,延伸的部份就讓看懂上面過程的人發揮吧 ;)

原始討論: http://twpug.net/x/modules/newbb/viewtopic.php?topic_id=3804

評論

Sign In or Register to comment.