2010-01-12 2 views
42

상황 :실용 Zend_ACL + Zend_Auth 구현 및 모범 사례

내 질문에 내가 거의 정확히 SO와 같은 개발하고 있어요 포럼에 관련된,가 여기서에 액세스 할 수

  1. 손님 보기 스레드하지만 회신하거나 투표를 할 수없는 충분한 담당자와 함께, 수정/다른 스레드를 투표, 기본적으로 그들은
  2. 관리자 사람 수 꽤 m 손님으로 동일한 권한을 회신 할 수 있습니다,
  3. 회원 uch do anything

이 ACL을 사이트 전체에 적용하고 기본적으로 모든 자원을 거부하고 싶습니다.

기본적으로 역할 (guest, member, admin)을 만들고 자원 (컨트롤러, 메소드)을 거부하거나 허용한다는 점에서 Zend_Acl 사용의 기본 사항을 읽었습니다. 문서는 당신이 실제로 응용 프로그램에서 ACL 코드를 구현하는 방법에 대한 매우 구체적인하지, 그래서 나는 그러나,


문제에 대한 몇 가지 실마리를 꽤 유용 유래 answer from marek 우연히 .. SO에보고했다 나의 생소한 것 때문에 나는 최선의 관행을 염두에두고 적절히 구현하는 방법을 아직 완전히 포악하게 할 수는 없다.

포스터는 역할을 추가, ACL에 객체를 초기화하는 응용 프로그램 루트에서 정적 파일 configAcl.php이있는 모든 컨트롤러에서 자원을 생성, 모든 것에 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 추상 클래스를 확장 한 새로운 클래스이며 auth/login에 연결될 수 있습니다. 기본적으로 로그인에 실패하면 논리가 리다이렉션됩니다. 그렇지 않으면 acl 객체가 레지스트리에서 ID를 수집하고 사용자가이 리소스를 볼 수 있는지 여부를 결정합니다.

$identity = $auth->getIdentity(); 

$frontController->registerPlugin(new AuthPlugin()); 

질문 # 2 - 정확히 어떻게 내가 실제로 사용자의 ID를 반환하는 인증 플러그인 부분을 코딩 할 것인가? 나는 그가 아래에 몇 가지 코드를 사용자 ID와 자격 증명 (해쉬 패스 검사)에 의해 데이터베이스 테이블의 열을 쿼리 Auth 어댑터 db 테이블 개체를 생성했다는 것을 알고 있습니다. 나는이 getIdentity 부분에 맞는 어디에 혼란스러워.

이의 내 사용자 테이블이 데이터 구성되었다 가정 해 봅시다 :

user_id user_name level 
1   superadmin 3 
2   john   2 
3   example.com 1 

경우 레벨 3 = 관리자, 2 = 멤버, 1 = 손님.

질문 3 - 정확히 위의 인증 코드를 넣기 좋은 곳은 어디입니까? 로그인 컨트롤러 내부에 있습니까?

질문 # 4 - ACL에 논리 모델 내부에서 수행되는 방법에 그의 기사와 다른 포스터 replies, 아직 그가이 가능하고, 기본적으로 지원되지 않는 사용하고 해결 방법을 필요로하는 특정 방법은? 그리고 이것이 실제로 어떻게 이상적으로 이루어져야 하는가?

답변

75

내 구현 :

질문 # 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(); 
    } 

} 

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); 
      } 
     } 
    } 
} 

class bootstrap 및 finnaly - Auth_Cont roller` :

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

DB 설계

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 &laquo;<?=$this->escape($snippetInfo['title'])?>&raquo; snippet">Edit</a> 
<?php endif?> 

질문이 있으십니까? :)

+0

대단히 감사합니다. 그 모든 것을 붙여 넣기 위해 고맙게 생각합니다. 구현 해보고 알려 드리겠습니다. 다시 한 번 감사드립니다. –

+0

이메일을 보내십시오. (구글, 전용) :) – SMka

+0

좋아,이게 내가 생각했던 것보다 나아. 한 질문. App_Plugin_Auth의 acl 객체가 검색 한 레지스트리에서 검색되지 않는 이유는 무엇입니까? 게시물을 주셔서 감사합니다 –