内容:
我的问题与我正在像SO这样开发的论坛有关,那里有:
我希望将此ACL应用于整个站点,并且默认情况下拒绝所有资源。
我阅读了使用Zend_Acl的基础知识- 您基本上可以创建角色(guest,member,admin),并拒绝或允许资源(控制器,方法)使用这些角色。该文档不是非常具体地说明如何在应用程序中实际实现ACL代码,因此我开始研究SO。
从marek那里得到了一个非常有用的答案,这为这个问题提供了一些启示,但是由于我不熟悉,我仍然无法完全理解如何正确地实现最佳实践。
张贴者configAcl.php在应用程序根目录中有一个静态文件,该文件初始化acl对象,添加角色,从每个控制器中创建资源,提供admin对所有内容的normal访问权限,对除admin之外的所有内容的访问权限,并将acl对象存储在注册表中以备后用。
configAcl.php
admin
normal
$acl = new Zend_Acl(); $roles = array('admin', 'normal'); // Controller script names. You have to add all of them if credential check // is global to your application. $controllers = array('auth', 'index', 'news', 'admin'); foreach ($roles as $role) { $acl->addRole(new Zend_Acl_Role($role)); } foreach ($controllers as $controller) { $acl->add(new Zend_Acl_Resource($controller)); } // Here comes credential definiton for admin user. $acl->allow('admin'); // Has access to everything. // Here comes credential definition for normal user. $acl->allow('normal'); // Has access to everything... $acl->deny('normal', 'admin'); // ... except the admin controller. // Finally I store whole ACL definition to registry for use // in AuthPlugin plugin. $registry = Zend_Registry::getInstance(); $registry->set('acl', $acl);
问题#1- 该代码应该在引导程序中,还是在这样的独立文件中?如果是这样的话,最好是在库目录之内?
它的第二部分是扩展Zend ControllerPluginAbstract类的新类,该类允许它被钩入auth/login,其逻辑基本上是如果登录失败,它将重定向..否则它将从注册表中获取acl对象,并获取标识,并确定是否允许用户查看此资源。
auth/login
$identity = $auth->getIdentity(); $frontController->registerPlugin(new AuthPlugin());
问题2- 我将如何对真正返回用户身份的auth插件部分进行编码?我意识到他下面有一些代码可以生成Auth适配器db表对象,该对象将通过用户ID和凭据(哈希的通过检查)来查询数据库表的列。.我对此与getIdentity部分的位置感到困惑。
假设我的用户表由以下数据组成:
user_id user_name level 1 superadmin 3 2 john 2 3 example.com 1
其中级别3 =管理员,级别2 =成员,级别1 =来宾。
问题#3- 将上述身份验证代码放入的确切位置是哪里?在登录控制器内部?
问题4- 另一位发帖人在他的文章中回答了应如何在模型内完成acl逻辑,但是他所使用的特定方法本身并不受支持,需要解决方法,这是否可行?这真的是理想的做法吗?
我的实现:
问题1
class App_Model_Acl extends Zend_Acl { const ROLE_GUEST = 'guest'; const ROLE_USER = 'user'; const ROLE_PUBLISHER = 'publisher'; const ROLE_EDITOR = 'editor'; const ROLE_ADMIN = 'admin'; const ROLE_GOD = 'god'; protected static $_instance; /* Singleton pattern */ protected function __construct() { $this->addRole(new Zend_Acl_Role(self::ROLE_GUEST)); $this->addRole(new Zend_Acl_Role(self::ROLE_USER), self::ROLE_GUEST); $this->addRole(new Zend_Acl_Role(self::ROLE_PUBLISHER), self::ROLE_USER); $this->addRole(new Zend_Acl_Role(self::ROLE_EDITOR), self::ROLE_PUBLISHER); $this->addRole(new Zend_Acl_Role(self::ROLE_ADMIN), self::ROLE_EDITOR); //unique role for superadmin $this->addRole(new Zend_Acl_Role(self::ROLE_GOD)); $this->allow(self::ROLE_GOD); /* Adding new resources */ $this->add(new Zend_Acl_Resource('mvc:users')) ->add(new Zend_Acl_Resource('mvc:users.auth'), 'mvc:users') ->add(new Zend_Acl_Resource('mvc:users.list'), 'mvc:users'); $this->allow(null, 'mvc:users', array('index', 'list')); $this->allow('guest', 'mvc:users.auth', array('index', 'login')); $this->allow('guest', 'mvc:users.list', array('index', 'list')); $this->deny(array('user'), 'mvc:users.auth', array('login')); /* Adding new resources */ $moduleResource = new Zend_Acl_Resource('mvc:snippets'); $this->add($moduleResource) ->add(new Zend_Acl_Resource('mvc:snippets.crud'), $moduleResource) ->add(new Zend_Acl_Resource('mvc:snippets.list'), $moduleResource); $this->allow(null, $moduleResource, array('index', 'list')); $this->allow('user', 'mvc:snippets.crud', array('create', 'update', 'delete', 'read', 'list')); $this->allow('guest', 'mvc:snippets.list', array('index', 'list')); return $this; } protected static $_user; public static function setUser(Users_Model_User $user = null) { if (null === $user) { throw new InvalidArgumentException('$user is null'); } self::$_user = $user; } /** * * @return App_Model_Acl */ public static function getInstance() { if (null === self::$_instance) { self::$_instance = new self(); } return self::$_instance; } public static function resetInstance() { self::$_instance = null; self::getInstance(); } } class Smapp extends Bootstrap // class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { /** * @var App_Model_User */ protected static $_currentUser; public function __construct($application) { parent::__construct($application); } public static function setCurrentUser(Users_Model_User $user) { self::$_currentUser = $user; } /** * @return App_Model_User */ public static function getCurrentUser() { if (null === self::$_currentUser) { self::setCurrentUser(Users_Service_User::getUserModel()); } return self::$_currentUser; } /** * @return App_Model_User */ public static function getCurrentUserId() { $user = self::getCurrentUser(); return $user->getId(); } }
在 class bootstrap
class bootstrap
protected function _initUser() { $auth = Zend_Auth::getInstance(); if ($auth->hasIdentity()) { if ($user = Users_Service_User::findOneByOpenId($auth->getIdentity())) { $userLastAccess = strtotime($user->last_access); //update the date of the last login time in 5 minutes if ((time() - $userLastAccess) > 60*5) { $date = new Zend_Date(); $user->last_access = $date->toString('YYYY-MM-dd HH:mm:ss'); $user->save(); } Smapp::setCurrentUser($user); } } return Smapp::getCurrentUser(); } protected function _initAcl() { $acl = App_Model_Acl::getInstance(); Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl($acl); Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole(Smapp::getCurrentUser()->role); Zend_Registry::set('Zend_Acl', $acl); return $acl; }
和 Front_Controller_Plugin
Front_Controller_Plugin
class App_Plugin_Auth extends Zend_Controller_Plugin_Abstract { private $_identity; /** * the acl object * * @var zend_acl */ private $_acl; /** * the page to direct to if there is a current * user but they do not have permission to access * the resource * * @var array */ private $_noacl = array('module' => 'admin', 'controller' => 'error', 'action' => 'no-auth'); /** * the page to direct to if there is not current user * * @var unknown_type */ private $_noauth = array('module' => 'users', 'controller' => 'auth', 'action' => 'login'); /** * validate the current user's request * * @param zend_controller_request $request */ public function preDispatch(Zend_Controller_Request_Abstract $request) { $this->_identity = Smapp::getCurrentUser(); $this->_acl = App_Model_Acl::getInstance(); if (!empty($this->_identity)) { $role = $this->_identity->role; } else { $role = null; } $controller = $request->controller; $module = $request->module; $controller = $controller; $action = $request->action; //go from more specific to less specific $moduleLevel = 'mvc:'.$module; $controllerLevel = $moduleLevel . '.' . $controller; $privelege = $action; if ($this->_acl->has($controllerLevel)) { $resource = $controllerLevel; } else { $resource = $moduleLevel; } if ($module != 'default' && $controller != 'index') { if ($this->_acl->has($resource) && !$this->_acl->isAllowed($role, $resource, $privelege)) { if (!$this->_identity) { $request->setModuleName($this->_noauth['module']); $request->setControllerName($this->_noauth['controller']); $request->setActionName($this->_noauth['action']); //$request->setParam('authPage', 'login'); } else { $request->setModuleName($this->_noacl['module']); $request->setControllerName($this->_noacl['controller']); $request->setActionName($this->_noacl['action']); //$request->setParam('authPage', 'noauth'); } throw new Exception('Access denied. ' . $resource . '::' . $role); } } } }
和最终-Auth_Controller` :)
class Users_AuthController extends Smapp_Controller_Action { //sesssion protected $_storage; public function getStorage() { if (null === $this->_storage) { $this->_storage = new Zend_Session_Namespace(__CLASS__); } return $this->_storage; } public function indexAction() { return $this->_forward('login'); } public function loginAction() { $openId = null; if ($this->getRequest()->isPost() and $openId = ($this->_getParam('openid_identifier', false))) { //do nothing } elseif (!isset($_GET['openid_mode'])) { return; } //$userService = $this->loadService('User'); $userService = new Users_Service_User(); $result = $userService->authenticate($openId, $this->getResponse()); if ($result->isValid()) { $identity = $result->getIdentity(); if (!$identity['Profile']['display_name']) { return $this->_helper->redirector->gotoSimpleAndExit('update', 'profile'); } $this->_redirect('/'); } else { $this->view->errorMessages = $result->getMessages(); } } public function logoutAction() { $auth = Zend_Auth::getInstance(); $auth->clearIdentity(); //Zend_Session::destroy(); $this->_redirect('/'); } }
问题2
放在里面Zend_Auth。
Zend_Auth
成功身份验证后-在存储中写入身份。 $auth->getStorage()->write($result->getIdentity());
$auth->getStorage()->write($result->getIdentity());
的identity-仅仅是user_id
identity
user_id
数据库设计
CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `open_id` varchar(255) NOT NULL, `role` varchar(20) NOT NULL, `last_access` datetime NOT NULL, `created_at` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `open_id` (`open_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `user_profile` ( `user_id` bigint(20) NOT NULL, `display_name` varchar(100) DEFAULT NULL, `email` varchar(100) DEFAULT NULL, `real_name` varchar(100) DEFAULT NULL, `website_url` varchar(255) DEFAULT NULL, `location` varchar(100) DEFAULT NULL, `birthday` date DEFAULT NULL, `about_me` text, `view_count` int(11) NOT NULL DEFAULT '0', `updated_at` datetime NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
一些糖
/** * SM's code library * * @category * @package * @subpackage * @copyright Copyright (c) 2009 Pavel V Egorov * @author Pavel V Egorov * @link http://epavel.ru/ * @since 08.09.2009 */ class Smapp_View_Helper_IsAllowed extends Zend_View_Helper_Abstract { protected $_acl; protected $_user; public function isAllowed($resource = null, $privelege = null) { return (bool) $this->getAcl()->isAllowed($this->getUser(), $resource, $privelege); } /** * @return App_Model_Acl */ public function getAcl() { if (null === $this->_acl) { $this->setAcl(App_Model_Acl::getInstance()); } return $this->_acl; } /** * @return App_View_Helper_IsAllowed */ public function setAcl(Zend_Acl $acl) { $this->_acl = $acl; return $this; } /** * @return Users_Model_User */ public function getUser() { if (null === $this->_user) { $this->setUser(Smapp::getCurrentUser()); } return $this->_user; } /** * @return App_View_Helper_IsAllowed */ public function setUser(Users_Model_User $user) { $this->_user = $user; return $this; } }
对于任何视图脚本中的此类事情
<?php if ($this->isAllowed('mvc:snippets.crud', 'update')) : ?> <a title="Edit «<?=$this->escape($snippetInfo['title'])?>» snippet">Edit</a> <?php endif?>
有什么问题吗 :)