语境:
我的问题涉及一个我正在开发的论坛,就像SO一样:
有权访问查看主题但无法回复或投票的访客
具有足够代表的成员可以编辑/投票其他线程,默认情况下,他们可以回复并拥有与来宾相同的权限
管理员几乎可以做任何事情
我希望此ACL在站点范围内应用,默认情况下拒绝所有资源.
我阅读了使用Zend_Acl的基础知识 - 您基本上创建角色(访客,成员,管理员),并拒绝或允许这些角色的资源(控制器,方法).关于如何在应用程序中实际实现acl代码的文档并不是非常具体,所以我继续关注SO ..
来自marek的一个非常有用的stackoverflow 答案揭示了这个问题,但由于我的不熟悉,我仍然不能完全理解如何正确地实现这一点,并考虑到最佳实践.
海报configAcl.php
在应用程序根目录中有一个静态文件,用于初始化acl对象,添加角色,从每个控制器创建资源,提供admin
对所有内容的normal
访问,访问除管理员之外的所有内容,并将acl对象存储在注册表中以供以后使用.
$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 Controller Plugin Abstract类的新类,它允许它被挂钩auth/login
,逻辑基本上如果登录失败,它会重定向..否则它从注册表中抓取acl对象,抓住标识,并确定是否允许用户查看此资源.
$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级= admin,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
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
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); } } } }
和finnaly - 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
.
在成功验证之后 - 在存储中写入身份. $auth->getStorage()->write($result->getIdentity());
的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; } }
对于任何视图脚本中的这类事情
isAllowed('mvc:snippets.crud', 'update')) : ?> Edit
有问题吗?:)