How to implement acl and authorization in Module.p

2019-07-24 20:05发布

I am working on Album Application in zf3.I added acl functionality to the application like this:

AlbumController.php

class AlbumController extends AbstractActionController
{
protected $role;
public function onDispatch(\Zend\Mvc\MvcEvent $e)
{
    $userSession = new Container('user');
    if (!isset($userSession->email)) {
        return $this->redirect()->toRoute('login');
    }
    else {
        $this->role = $userSession->role;
        parent::onDispatch($e);
      }
    }
public function checkPermission($role,$action)
{
  if($role == 'admin'){
    $acl = new Acl();
    if ($acl->isAllowed('admin', 'AlbumController', $action)) {
        return true;
    }
  }
  return false;
}
public function editAction()
{
  $action = 'edit';
  $permission = $this->checkPermission($this->role,$action);
  if (!$permission) {
      $this->flashMessenger()->addMessage('<div class="alert alert-  danger" role="alert"><b>You dont have the privilege to edit!!</b></div>');
      return $this->redirect()->toRoute('album');
  }
  $id = (int) $this->params()->fromRoute('id', 0);

    if (0 === $id) {
        return $this->redirect()->toRoute('album', ['action' => 'add']);
    }
    try {
        $album = $this->table->getAlbum($id);
    } catch (\Exception $e) {
        return $this->redirect()->toRoute('album', ['action' => 'index']);
    }

    $form = new AlbumForm();
    $form->bind($album);
    $form->get('submit')->setAttribute('value', 'Edit');

    $request = $this->getRequest();
    $viewData = ['id' => $id, 'form' => $form];
    if (! $request->isPost()) {
        return $viewData;
    }

    $form->setInputFilter($album->getInputFilter());
    $form->setData($request->getPost());
    $edit = $request->getPost('submit', 'Cancel');
    if($edit == 'Cancel'){
      $this->flashMessenger()->addMessage('<div class="alert alert-danger" role="alert"><b>Cancelled by User...!!</b></div>');
      return $this->redirect()->toRoute('album');
    }
    if (! $form->isValid()) {
        $this->flashMessenger()->addMessage('<div class="alert alert-danger" role="alert"><b>Failed to Update...!!</b></div>');
        return $viewData;
    }else{
      $this->table->saveAlbum($album);
      $this->flashMessenger()->addMessage('<div class="alert alert-success" role="alert"><b>Record Updated Successfully...!!</b></div>');
    }
    // Redirect to album list
    return $this->redirect()->toRoute('album', ['action' => 'index']);
}
}

This is working perfectly fine,now i want to move the onDispatch function to Module.php but don't know how to implement it.Can someone please help me

Module.php

<?php
 namespace Album;

 use Album\Controller\AlbumController;
 use Album\Model\Album;
 use Album\Model\AlbumTable;
 use Zend\Db\Adapter\AdapterInterface;
 use Zend\Db\ResultSet\ResultSet;
 use Zend\Db\TableGateway\TableGateway;
 use Zend\ModuleManager\Feature\ConfigProviderInterface;
 use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
 use Album\Model\LoginTable;

 class Module implements ConfigProviderInterface
 {
  public function getConfig()
  {
     return include __DIR__ . '/../config/module.config.php';
  }

  public function getServiceConfig()
    {
    return [
        'factories' => [
            AlbumTable::class => function($container) {

                $tableGateway = $container->get(Model\AlbumTableGateway::class);
                return new AlbumTable($tableGateway);
            },
            Model\AlbumTableGateway::class => function ($container) {

                $dbAdapter = $container->get(AdapterInterface::class);

                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(new Album());
                return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);
            },
            Model\LoginTable::class =>  function($container) {
            $tableGateway = $container->get(Model\LoginTableGateway::class);
            $table = new LoginTable($tableGateway);
            return $table;
           },
                    Model\LoginTableGateway::class => function ($container){
                     $dbAdapter = $container->get(AdapterInterface::class);
                     $resultSetPrototype = new ResultSet();
                     return new TableGateway('login', $dbAdapter, null, $resultSetPrototype);
                     }
           ],
            ];
}

 public function getControllerConfig()
{
    return [
        'factories' => [
            Controller\AlbumController::class => function($container) {
              return new Controller\AlbumController($container->get(Model\AlbumTable::class));
            },
            Controller\LoginController::class => function($container)  {
              return new Controller\LoginController($container->get(Model\LoginTable::class));
            },
            Controller\LogoutController::class => function($container){
              return new Controller\LogoutController($container->get(Model\LoginTable::class));
            },
         ],
    ];
}
}

1条回答
淡お忘
2楼-- · 2019-07-24 20:31

This is how I implemented it. In your Module.php, add a listener on EVENT_DISPATCH, with a closure as callback that will call your middleware class authorization :

class Module implements ConfigProviderInterface
{
    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    public function onBootstrap(MvcEvent $e)
    {
        $app = $e->getApplication();
        $eventManager = $app->getEventManager();
        $serviceManager = $app->getServiceManager();

        // Register closure on event DISPATCH, call your checkProtectedRoutes() method
        $eventManager->attach(MvcEvent::EVENT_DISPATCH, function (MvcEvent $e) use ($serviceManager) {
            $match = $e->getRouteMatch();
            $auth = $serviceManager->get(Middleware\AuthorizationMiddleware::class);
            $res = $auth->checkProtectedRoutes($match);
            if ($res instanceof Response) {
                return $res;
            }
        }, 1);

        // Init ACL : could be improved
        $this->initAcl($e);
    }

You should have an AuthorizationMiddlewareFactory (call it as you want):

<?php

namespace MyModule\Factory;

use Interop\Container\ContainerInterface;
use MyModule\Middleware\AuthorizationMiddleware;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;

class AuthorizationMiddlewareFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $authService = $container->get(AuthenticationService::class);
        $acl = $container->get('Acl');  // II init it in bootstrap(), could be improved
        $response = $container->get('Response');
        $baseUrl = $container->get('Request')->getBaseUrl();
        $authorization = new AuthorizationMiddleware($authService, $acl, $response, $baseUrl);
        return $authorization ;
    }
}

And your AuthorizationMiddleware class:

<?php

namespace MyModule\Middleware;

use Symfony\Component\VarDumper\VarDumper;
use Zend\Authentication\AuthenticationService;
use Zend\Http\PhpEnvironment\Response;
use Zend\Permissions\Acl\Acl;
use Zend\Router\RouteMatch;

class AuthorizationMiddleware
{
    private $authService ;
    private $acl;
    private $response;
    private $baseUrl;

    /**
     * AuthorizationMiddleware constructor.
     * @param AuthenticationService $authService
     * @param Acl $acl
     * @param Response $response
     * @param $baseUrl
     */
    public function __construct(AuthenticationService $authService, Acl $acl, Response $response, $baseUrl)
    {
        $this->authService = $authService;
        $this->acl = $acl;
        $this->response = $response;
        $this->baseUrl = $baseUrl;
    }

    public function checkProtectedRoutes(RouteMatch $match)
    {
        if (! $match) {
            // Nothing without a route
            return null ;
        }
        // Do your checks...
}

It can be improved, but you have the idea... See also this Question and the answers: ZF3 redirection after ACL authorization failed

查看更多
登录 后发表回答