Using Zend_Auth to secure all controllers

2019-02-09 22:06发布

问题:

How would i globally secure all my controllers (except for my Login controller) to ensure my application is secure at all points (no hidden backdoor to ajax calls, etc). I thought that I might put it in my bootstrap file, but this doesn't feel right? I'm trying to avoid adding any code to each controller.

Suggestions?

回答1:

edit: this is a complement of @singles response.

You must understand there are 2 different things. Auth and Acl. Auth tells you who is the user, and you can for example redirect user having no Auth to you login controller, and set an auth identity after login. then the Acl system take yes/no decisions based on the Auth data (could be the user id or is role, stored in the Auth storage.

On nice solution is to have 2 controllers plugins (registered in the good order on the bootstrap, Auth then Acl). If you do not use Controller plugins you'll have to call the Acl check in each controller, when needed. If you always need it, then use plugins.

Implement the preDispatch() in you Auth plugin to set for example an anonymous identity if you have no identity return from Zend_Auth. This is a code snippet of a real one:

public function preDispatch(Zend_Controller_Request_Abstract $request)
{
    $module = $request->getModuleName();
    $controller = $request->getControllerName();
    $action = $request->getActionName();
    $auth = Zend_Auth::getInstance();
    if (!$auth->hasIdentity()) {
        // set a default anonymous identity
        $auth->getStorage()->write(array('name' => 'anonymous','role' => 1,));
    }
(...)

And for the Acl controller plugin the task is as well in preDispatch(). You can launch an acl check for each requested url (so for each user request, even ajax). Here's a partial snippet, so this just an example of how you could handle things:

public function preDispatch(Zend_Controller_Request_Abstract $request) {
    $controller = $request->controller;
    $module = $request->module;
    $action = $request->action;
    // here you should code something nice retrieving you Zend_Acl object
    // with some caching options maybe, building roles, ressources, etc
    $this->_acl = $this->getAcl(); 
    if (!$this->_acl->isCurrentUserAllowed($module,'see')) {
        $auth = Zend_Auth::getInstance();
    $identity  = $auth->hasIdentity('identity')? $auth->getIdentity() : null;
    if(isset($identity)) {
            if($identity['name'] == 'anonymous') {
                // WARNING: avoid infinite redirect loops on login page
                if (!($request->getControllerName() == 'login' 
                    && $request->getActionName()=='login' 
                    && $request->getModuleName() == 'default')) {
                        $request->setControllerName('login')
               ->setActionName('login')
               ->setModuleName('default');
            return;
(...)

and in this system the last important part is the LoginController where in case of succesful login you should initate the identity record:

(...)
$auth = Zend_Auth::getInstance();
Zend_Session::regenerateId();
$storage = $auth->getStorage();
$rowobject = $authAdapter->getResultRowObject(null,'passwd');
$storage->write((array)$rowobject);
(...)


回答2:

You should write ACL plugin for that and register it in front controller. If you implement such functionality as a plugin you will have flexibility to use it in your next application - without need to extend each controller from your custom controller.

Resources:
1. Front Controller Plugins in Zend Framework - how plugins work in ZF
2. Zend_Acl / Zend_Auth example scenario - one of many possible implementations of ACL plugin.
3. Google - and lot of another resources



回答3:

Well, you need to have an entry point to your system :)

This can be achieved by extending Zend_Controller_Action, let's say:

abstract class MyController extends Zend_Controller_Action {
  public function preDispatch(){
    // do the logic
  }
} 

And now each of your controllers only needs to extend MyController and you're secure.



回答4:

The way I did it in one implementation was to create a file called Auth.php in my application path. Then I opened up every controller I wanted to be protected and added the line

include_once APPLICATION_PATH . '/Auth.php';

to the init() method before calling parent::init().

As for the Auth.php file itself, it basically uses Zend_Auth to authenticate. On success I would save the identity of the user for later use in the application

$this->view->assign('myIdentity', Zend_Auth::getInstance()->getIdentity());

On failure I would redirect to the login page and pass some params so that the login page knows where to send me once I'm logged in.

This is not an elegant solution but it's reliable and relatively quick and easy to implement.