参考:http://p0l0.binware.org/index.php/2012/02/18/zend-framework-2-authentication-acl-using-eventmanager/
First I created an extra config for ACL (could be also in module.config.php, but I prefer to have it in a separated file)
config/acl.config.php
01 <!--?php
02 return array(
03 'acl' =--> array(
04 'roles' => array(
05 'guest' => null,
06 'member' => 'guest'
07 ),
08 'resources' => array(
09 'allow' => array(
10 'user' => array(
11 'login' => 'guest',
12 'all' => 'member'
13 )
14 )
15 )
16 )
17 );
Next step is to create the Acl Class where we manage the roles and resources which we have defined in the config
src/User/Acl/Acl.php
001 <!--?php
002 /**
003 * File for Acl Class
004 *
005 * @category User
006 * @package User_Acl
007 * @author Marco Neumann <webcoder_at_binware_dot_org-->
008 * @copyright Copyright (c) 2011, Marco Neumann
009 * @license http://binware.org/license/index/type:new-bsd New BSD License
010 */
011
012 /**
013 * @namespace
014 */
015 namespace User\Acl;
016
017 /**
018 * @uses Zend\Acl\Acl
019 * @uses Zend\Acl\Role\GenericRole
020 * @uses Zend\Acl\Resource\GenericResource
021 */
022 use Zend\Acl\Acl as ZendAcl,
023 Zend\Acl\Role\GenericRole as Role,
024 Zend\Acl\Resource\GenericResource as Resource;
025
026 /**
027 * Class to handle Acl
028 *
029 * This class is for loading ACL defined in a config
030 *
031 * @category User
032 * @package User_Acl
033 * @copyright Copyright (c) 2011, Marco Neumann
034 * @license http://binware.org/license/index/type:new-bsd New BSD License
035 */
036 class Acl extends ZendAcl {
037 /**
038 * Default Role
039 */
040 const DEFAULT_ROLE = 'guest';
041
042 /**
043 * Constructor
044 *
045 * @param array $config
046 * @return void
047 * @throws \Exception
048 */
049 public function __construct($config)
050 {
051 if (!isset($config['acl']['roles']) || !isset($config['acl']['resources'])) {
052 throw new \Exception('Invalid ACL Config found');
053 }
054
055 $roles = $config['acl']['roles'];
056 if (!isset($roles[self::DEFAULT_ROLE])) {
057 $roles[self::DEFAULT_ROLE] = '';
058 }
059
060 $this->_addRoles($roles)
061 ->_addResources($config['acl']['resources']);
062 }
063
064 /**
065 * Adds Roles to ACL
066 *
067 * @param array $roles
068 * @return User\Acl
069 */
070 protected function _addRoles($roles)
071 {
072 foreach ($roles as $name => $parent) {
073 if (!$this->hasRole($name)) {
074 if (empty($parent)) {
075 $parent = array();
076 } else {
077 $parent = explode(',', $parent);
078 }
079
080 $this->addRole(new Role($name), $parent);
081 }
082 }
083
084 return $this;
085 }
086
087 /**
088 * Adds Resources to ACL
089 *
090 * @param $resources
091 * @return User\Acl
092 * @throws \Exception
093 */
094 protected function _addResources($resources)
095 {
096 foreach ($resources as $permission => $controllers) {
097 foreach ($controllers as $controller => $actions) {
098 if ($controller == 'all') {
099 $controller = null;
100 } else {
101 if (!$this->hasResource($controller)) {
102 $this->addResource(new Resource($controller));
103 }
104 }
105
106 foreach ($actions as $action => $role) {
107 if ($action == 'all') {
108 $action = null;
109 }
110
111 if ($permission == 'allow') {
112 $this->allow($role, $controller, $action);
113 } elseif ($permission == 'deny') {
114 $this->deny($role, $controller, $action);
115 } else {
116 throw new \Exception('No valid permission defined: ' . $permission);
117 }
118 }
119 }
120 }
121
122 return $this;
123 }
124 }
I decided to use a Controller Plugin (zf1 ActionHelpers) for the Authentication interaction
src/User/Controller/Plugin/UserAuthentication.php
001 <!--?php
002 /**
003 * File for UserAuthentication Class
004 *
005 * @category User
006 * @package User_Controller
007 * @subpackage User_Controller_Plugin
008 * @author Marco Neumann <webcoder_at_binware_dot_org-->
009 * @copyright Copyright (c) 2011, Marco Neumann
010 * @license http://binware.org/license/index/type:new-bsd New BSD License
011 */
012
013 /**
014 * @namespace
015 */
016 namespace User\Controller\Plugin;
017
018 /**
019 * @uses Zend\Mvc\Controller\Plugin\AbstractPlugin
020 * @uses Zend\Authentication\AuthenticationService
021 * @uses Zend\Authentication\Adapter\DbTable
022 */
023 use Zend\Mvc\Controller\Plugin\AbstractPlugin,
024 Zend\Authentication\AuthenticationService,
025 Zend\Authentication\Adapter\DbTable as AuthAdapter;
026
027 /**
028 * Class for User Authentication
029 *
030 * Handles Auth Adapter and Auth Service to check Identity
031 *
032 * @category User
033 * @package User_Controller
034 * @subpackage User_Controller_Plugin
035 * @copyright Copyright (c) 2011, Marco Neumann
036 * @license http://binware.org/license/index/type:new-bsd New BSD License
037 */
038 class UserAuthentication extends AbstractPlugin
039 {
040 /**
041 * @var AuthAdapter
042 */
043 protected $_authAdapter = null;
044
045 /**
046 * @var AuthenticationService
047 */
048 protected $_authService = null;
049
050 /**
051 * Check if Identity is present
052 *
053 * @return bool
054 */
055 public function hasIdentity()
056 {
057 return $this->getAuthService()->hasIdentity();
058 }
059
060 /**
061 * Return current Identity
062 *
063 * @return mixed|null
064 */
065 public function getIdentity()
066 {
067 return $this->getAuthService()->getIdentity();
068 }
069
070 /**
071 * Sets Auth Adapter
072 *
073 * @param \Zend\Authentication\Adapter\DbTable $authAdapter
074 * @return UserAuthentication
075 */
076 public function setAuthAdapter(AuthAdapter $authAdapter)
077 {
078 $this->_authAdapter = $authAdapter;
079
080 return $this;
081 }
082
083 /**
084 * Returns Auth Adapter
085 *
086 * @return \Zend\Authentication\Adapter\DbTable
087 */
088 public function getAuthAdapter()
089 {
090 if ($this->_authAdapter === null) {
091 $this->setAuthAdapter(new AuthAdapter());
092 }
093
094 return $this->_authAdapter;
095 }
096
097 /**
098 * Sets Auth Service
099 *
100 * @param \Zend\Authentication\AuthenticationService $authService
101 * @return UserAuthentication
102 */
103 public function setAuthService(AuthenticationService $authService)
104 {
105 $this->_authService = $authService;
106
107 return $this;
108 }
109
110 /**
111 * Gets Auth Service
112 *
113 * @return \Zend\Authentication\AuthenticationService
114 */
115 public function getAuthService()
116 {
117 if ($this->_authService === null) {
118 $this->setAuthService(new AuthenticationService());
119 }
120
121 return $this->_authService;
122 }
123
124 }
Now we can create the Event Handler were we will implement the AuthenticationPlugin (at this point, I think that using a Controller Plugin is not the right way to do it, but for now I will leave it so until I have a better solution…) and the Acl
src/Event/Authentication.php
001 <!--?php
002 /**
003 * File for Event Class
004 *
005 * @category User
006 * @package User_Event
007 * @author Marco Neumann <webcoder_at_binware_dot_org-->
008 * @copyright Copyright (c) 2011, Marco Neumann
009 * @license http://binware.org/license/index/type:new-bsd New BSD License
010 */
011
012 /**
013 * @namespace
014 */
015 namespace User\Event;
016
017 /**
018 * @uses Zend\Mvc\MvcEvent
019 * @uses User\Controller\Plugin\UserAuthentication
020 * @uses User\Acl\Acl
021 */
022 use Zend\Mvc\MvcEvent as MvcEvent,
023 User\Controller\Plugin\UserAuthentication as AuthPlugin,
024 User\Acl\Acl as AclClass;
025
026 /**
027 * Authentication Event Handler Class
028 *
029 * This Event Handles Authentication
030 *
031 * @category User
032 * @package User_Event
033 * @copyright Copyright (c) 2011, Marco Neumann
034 * @license http://binware.org/license/index/type:new-bsd New BSD License
035 */
036 class Authentication
037 {
038 /**
039 * @var AuthPlugin
040 */
041 protected $_userAuth = null;
042
043 /**
044 * @var AclClass
045 */
046 protected $_aclClass = null;
047
048 /**
049 * preDispatch Event Handler
050 *
051 * @param \Zend\Mvc\MvcEvent $event
052 * @throws \Exception
053 */
054 public function preDispatch(MvcEvent $event)
055 {
056 //@todo - Should we really use here and Controller Plugin?
057 $userAuth = $this->getUserAuthenticationPlugin();
058 $acl = $this->getAclClass();
059 $role = AclClass::DEFAULT_ROLE;
060
061 if ($userAuth->hasIdentity()) {
062 $user = $userAuth->getIdentity();
063 $role = 'member'; //@todo - Get role from user!
064 }
065
066
067 $routeMatch = $event->getRouteMatch();
068 $controller = $routeMatch->getParam('controller');
069 $action = $routeMatch->getParam('action');
070
071 if (!$acl->hasResource($controller)) {
072 throw new \Exception('Resource ' . $controller . ' not defined');
073 }
074
075 if (!$acl->isAllowed($role, $controller, $action)) {
076 $url = $event->getRouter()->assemble(array(), array('name' => 'login'));
077 $response = $event->getResponse();
078 $response->headers()->addHeaderLine('Location', $url);
079 $response->setStatusCode(302);
080 $response->sendHeaders();
081 exit;
082 }
083 }
084
085 /**
086 * Sets Authentication Plugin
087 *
088 * @param \User\Controller\Plugin\UserAuthentication $userAuthenticationPlugin
089 * @return Authentication
090 */
091 public function setUserAuthenticationPlugin(AuthPlugin $userAuthenticationPlugin)
092 {
093 $this->_userAuth = $userAuthenticationPlugin;
094
095 return $this;
096 }
097
098 /**
099 * Gets Authentication Plugin
100 *
101 * @return \User\Controller\Plugin\UserAuthentication
102 */
103 public function getUserAuthenticationPlugin()
104 {
105 if ($this->_userAuth === null) {
106 $this->_userAuth = new AuthPlugin();
107 }
108
109 return $this->_userAuth;
110 }
111
112 /**
113 * Sets ACL Class
114 *
115 * @param \User\Acl\Acl $aclClass
116 * @return Authentication
117 */
118 public function setAclClass(AclClass $aclClass)
119 {
120 $this->_aclClass = $aclClass;
121
122 return $this;
123 }
124
125 /**
126 * Gets ACL Class
127 *
128 * @return \User\Acl\Acl
129 */
130 public function getAclClass()
131 {
132 if ($this->_aclClass === null) {
133 $this->_aclClass = new AclClass(array());
134 }
135
136 return $this->_aclClass;
137 }
138 }
Now we need to attach the Event Handler to the EventManager, this will be done in the Module Class (haven’t found a better way to do it). I also had to attach an own function and from there trigger the User\Event\Authentication function (not very elegant ;( )
Module.php
01 <!--?php
02 /**
03 * File for Module Class
04 *
05 * @category User
06 * @package User
07 * @author Marco Neumann <webcoder_at_binware_dot_org-->
08 * @copyright Copyright (c) 2011, Marco Neumann
09 * @license http://binware.org/license/index/type:new-bsd New BSD License
10 */
11
12 /**
13 * @namespace
14 */
15 namespace User;
16
17 /**
18 * @uses Zend\Module\Consumer\AutoloaderProvider
19 * @uses Zend\EventManager\StaticEventManager
20 */
21 use Zend\Module\Consumer\AutoloaderProvider,
22 Zend\EventManager\StaticEventManager;
23
24 /**
25 * Module Class
26 *
27 * Handles Module Initialization
28 *
29 * @category User
30 * @package User
31 * @copyright Copyright (c) 2011, Marco Neumann
32 * @license http://binware.org/license/index/type:new-bsd New BSD License
33 */
34 class Module implements AutoloaderProvider
35 {
36 /**
37 * Init function
38 *
39 * @return void
40 */
41 public function init()
42 {
43 // Attach Event to EventManager
44 $events = StaticEventManager::getInstance();
45 $events->attach('Zend\Mvc\Controller\ActionController', 'dispatch', array($this, 'mvcPreDispatch'), 100); //@todo - Go directly to User\Event\Authentication
46 }
47
48 /**
49 * Get Autoloader Configuration
50 *
51 * @return array
52 */
53 public function getAutoloaderConfig()
54 {
55 return array(
56 'Zend\Loader\ClassMapAutoloader' => array(
57 __DIR__ . '/autoload_classmap.php'
58 ),
59 'Zend\Loader\StandardAutoloader' => array(
60 'namespaces' => array(
61 __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__
62 )
63 )
64 );
65 }
66
67 /**
68 * Get Module Configuration
69 *
70 * @return array
71 */
72 public function getConfig()
73 {
74 return include __DIR__ . '/config/module.config.php';
75 }
76
77 /**
78 * MVC preDispatch Event
79 *
80 * @param $event
81 * @return mixed
82 */
83 public function mvcPreDispatch($event) {
84 $di = $event->getTarget()->getLocator();
85 $auth = $di->get('User\Event\Authentication');
86
87 return $auth->preDispatch($event);
88 }
89 }
Here is the config where we attach all needed classes and load acl.config.php
config/module.config.php
01 <!--?php
02 return array(
03 'di' =--> array(
04 'instance' => array(
05 'alias' => array(
06 'user' => 'User\Controller\UserController'
07 ),
08 'user' => array(
09 'parameters' => array(
10 'broker' => 'Zend\Mvc\Controller\PluginBroker'
11 )
12 ),
13 'User\Event\Authentication' => array(
14 'parameters' => array(
15 'userAuthenticationPlugin' => 'User\Controller\Plugin\UserAuthentication',
16 'aclClass' => 'User\Acl\Acl'
17 )
18 ),
19 'User\Acl\Acl' => array(
20 'parameters' => array(
21 'config' => include __DIR__ . '/acl.config.php'
22 )
23 ),
24 'User\Controller\Plugin\UserAuthentication' => array(
25 'parameters' => array(
26 'authAdapter' => 'Zend\Authentication\Adapter\DbTable'
27 )
28 ),
29 'Zend\Authentication\Adapter\DbTable' => array(
30 'parameters' => array(
31 'zendDb' => 'Zend\Db\Adapter\Mysqli',
32 'tableName' => 'users',
33 'identityColumn' => 'email',
34 'credentialColumn' => 'password',
35 'credentialTreatment' => 'SHA1(CONCAT(?, "secretKey"))'
36 )
37 ),
38 'Zend\Db\Adapter\Mysqli' => array(
39 'parameters' => array(
40 'config' => array(
41 'host' => 'localhost',
42 'username' => 'username',
43 'password' => 'password',
44 'dbname' => 'dbname',
45 'charset' => 'utf-8'
46 )
47 )
48 ),
49 'Zend\Mvc\Controller\PluginLoader' => array(
50 'parameters' => array(
51 'map' => array(
52 'userAuthentication' => 'User\Controller\Plugin\UserAuthentication'
53 )
54 )
55 ),
56 'Zend\View\PhpRenderer' => array(
57 'parameters' => array(
58 'options' => array(
59 'script_paths' => array(
60 'user' => __DIR__ . '/../views'
61 )
62 )
63 )
64 )
65 )
66 ),
67 'routes' => array(
68 'login' => array(
69 'type' => 'Zend\Mvc\Router\Http\Literal',
70 'options' => array(
71 'route' => '/login',
72 'defaults' => array(
73 'controller' => 'user',
74 'action' => 'login',
75 )
76 )
77 )
78 )
79 );
Well, we are now done, we only need to create the Login Form and the Controller
src/Form/Login.php
01 <!--?php
02 /**
03 * File for Login Form Class
04 *
05 * @category User
06 * @package User_Form
07 * @author Marco Neumann <webcoder_at_binware_dot_org-->
08 * @copyright Copyright (c) 2011, Marco Neumann
09 * @license http://binware.org/license/index/type:new-bsd New BSD License
10 */
11
12 /**
13 * @namespace
14 */
15 namespace User\Form;
16
17 /**
18 * @uses Zend\Form\Form
19 */
20 use Zend\Form\Form;
21
22 /**
23 * Login Form Class
24 *
25 * Login Form
26 *
27 * @category User
28 * @package User_Form
29 * @copyright Copyright (c) 2011, Marco Neumann
30 * @license http://binware.org/license/index/type:new-bsd New BSD License
31 */
32 class Login extends Form
33 {
34 /**
35 * Initialize Form
36 */
37 public function init()
38 {
39 $this->setMethod('post')
40 ->loadDefaultDecorators()
41 ->addDecorator('FormErrors');
42
43 $this->addElement(
44 'text',
45 'username',
46 array(
47 'filters' => array(
48 array('StringTrim')
49 ),
50 'validators' => array(
51 'EmailAddress'
52 ),
53 'required' => true,
54 'label' => 'Email'
55 )
56 );
57
58 $this->addElement(
59 'password',
60 'password',
61 array(
62 'filters' => array(
63 array('StringTrim')
64 ),
65 'validators' => array(
66 array(
67 'StringLength',
68 true,
69 array(
70 6,
71 999
72 )
73 )
74 ),
75 'required' => true,
76 'label' => 'Password'
77 )
78 );
79
80 $this->addElement(
81 'hash',
82 'csrf',
83 array(
84 'ignore' => true,
85 'decorators' => array('ViewHelper')
86 )
87 );
88
89 $this->addElement(
90 'submit',
91 'login',
92 array(
93 'ignore' => true,
94 'label' => 'Login'
95 )
96 );
97
98 }
99 }
src/User/Controller/UserController.php
01 <!--?php
02 /**
03 * File for User Controller Class
04 *
05 * @category User
06 * @package User_Controller
07 * @author Marco Neumann <webcoder_at_binware_dot_org-->
08 * @copyright Copyright (c) 2011, Marco Neumann
09 * @license http://binware.org/license/index/type:new-bsd New BSD License
10 */
11
12 /**
13 * @namespace
14 */
15 namespace User\Controller;
16
17 /**
18 * @uses Zend\Mvc\Controller\ActionController
19 * @uses User\Form\Login
20 */
21 use Zend\Mvc\Controller\ActionController,
22 User\Form\Login as LoginForm;
23
24 /**
25 * User Controller Class
26 *
27 * User Controller
28 *
29 * @category User
30 * @package User_Controller
31 * @copyright Copyright (c) 2011, Marco Neumann
32 * @license http://binware.org/license/index/type:new-bsd New BSD License
33 */
34 class UserController extends ActionController
35 {
36 /**
37 * Index Action
38 */
39 public function indexAction()
40 {
41 //@todo - Implement indexAction
42 }
43
44 /**
45 * Login Action
46 *
47 * @return array
48 */
49 public function loginAction()
50 {
51 $form = new LoginForm();
52 $request = $this->getRequest();
53
54 if ($request->isPost() && $form->isValid($request->post()->toArray())) {
55 $uAuth = $this->getLocator()->get('User\Controller\Plugin\UserAuthentication'); //@todo - We must use PluginLoader $this->userAuthentication()!!
56 $authAdapter = $uAuth->getAuthAdapter();
57
58 $authAdapter->setIdentity($form->getValue('username'));
59 $authAdapter->setCredential($form->getValue('password'));
60
61 \Zend\Debug::dump($uAuth->getAuthService()->authenticate($authAdapter));
62 }
63
64 return array('loginForm' => $form);
65 }
66
67 /**
68 * Logout Action
69 */
70 public function logoutAction()
71 {
72 $this->getLocator()->get('User\Controller\Plugin\UserAuthentication')->clearIdentity(); //@todo - We must use PluginLoader $this->userAuthentication()!!
73 }
74 }