Forward to another controller/action from module.p

2019-02-27 16:20发布

问题:

I am writing a module for ACL in ZF2, And I am almost done with it.

The point where I am stucked is when user is not authorised to access the requested page, I want to forward the user to a page showing 403 message.

I have tried redirecting user to 403 but it updates URL, so now I am tring to forward user.

All I want to do is from Module.php. I have tried below code -

Module.php

if (!$isAllowed) {
    $e->getApplication()->getServiceManager()->get('ControllerPluginManager')->get('forward')->dispatch('acl');
}

Using this I got following error -

Uncaught exception 'Zend\Mvc\Exception\DomainException' with message 'Forward plugin requires a controller that implements InjectApplicationEventInterface'

I have also tried to implement Acl controller with InjectApplicationEventInterface, But the issue remains same.

Can you please explain how to Forward to another Action from Module.php?
Let me know if you need more details.

回答1:

What you can do is to listen for the dispatch event. You can update the route match during this event to match a controller/action pair which is defined by yourself to render the 403 page.

In code:

use MvcEvent;

class Module
{
    public function onBootstrap($e)
    {
        $app = $e->getApplication();
        $acl = $app->getServiceManager()->get('ACL'); // get your ACL here

        if (!$acl->isAllowed()) {
            $em = $app->getEventManager();
            $em->attach(MvcEvent::EVENT_DISPATCH, function($e) {
                $routeMatch = $e->getRouteMatch();

                $routeMatch->setParam('controller', 'my-403-controller');
                $routeMatch->setParam('action', 'my-403-action');
            }, 1000);
        }
    }
}

Forwarding is a pattern to dispatch a controller when another controller has already been dispatched. That is not your case, as I read it from your question. So don't use the forward plugin, but modify the route match before it's dispached.



回答2:

You can not forword but redirect to your 403 page as follow:

if (!$acl->isAllowed()) {
    $response = $e->getResponse();
    $response->getHeaders()->addHeaderLine('Location', $e->getRequest()->getBaseUrl() . '/403page');
    $response->setStatusCode(403);
}


回答3:

I think that if you want to send information to user that he is in restricted zone without the update of url you have to:

  1. Change layout which shows restricted area message.
  2. Leave original layout and just switch templates for your action so it shows restricted area message.

After one of this actions you just change Response to 403. If that`s what you want its simple to do. For any action in your controller you want to send 403 status just use:

 $viewModel->setTemplate('partial/noRights');
 $this->getResponse()->setStatusCode('403');
 return $viewModel;

or if you want to change layout for your custom one:

 $this->layout('layout/custom');
 $this->getResponse()->setStatusCode('403');
 return $viewModel;

You can of course do it to in bootstrap section of your module adding listener on event_dispatch and there check if $acl->isAllowed() after that do your changes which i wrote above. Example:

public function onBootstrap(MvcEvent $e)
{
    $app = $e->getApplication();
    $acl = $app->getServiceManager()->get('ACL'); // get your ACL here

    if (!$acl->isAllowed()) {
        $eventManager = $app->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager();
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, function($e) {
            $controller = $e->getTarget(); //controller`s action which triggered event_dispatch
            $controller->getResponse()->setStatusCode('403');
            $controller->layout('layout/custom');
        }, 1000);
    }
}


回答4:

When I implemented ACL, I created my own AUTH module for authorization and authentication.
In that module I created ACL plugin.. something like below

//Module.php

 /**
     * This method is called once the MVC bootstrapping is complete
     * 
     * @param \Zend\EventManager\EventInterface $e
     */
    public function onBootstrap(Event $e) 
    {
        $services = $e->getApplication()->getServiceManager();

        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach('dispatch', array($this, 'loadConfiguration'), 101);
    }

    /**
     * 
     * @param \Zend\Mvc\MvcEvent $e
     */
    public function loadConfiguration(MvcEvent $e) 
    {        
        $e->getApplication()->getServiceManager()
                ->get('ControllerPluginManager')->get('AclPlugin')
                ->checkAcl($e); //Auth/src/Auth/Controller/AclPlugin      
    }

//Auth/src/Auth/Controller/AclPlugin

namespace Auth\Controller\Plugin;

/**
 * load libraries here
 */
class AclPlugin extends AbstractPlugin implements ServiceManagerAwareInterface 
{
    /*
     * @var Doctrine\ORM\EntityManager
     */

    protected $em;
    protected $sm;

    /**
     * @param Doctrine\ORM\EntityManager $em
     * @return string
     */
public function checkAcl($e) 
    {

        $matches = $e->getRouteMatch();
        $controller = $matches->getParam('controller');
        $action = $matches->getParam('action', 'index');

       if ($acl->isAllowed($role, $resource, $permission)) {


            return;

        } else {
            $matches->setParam('controller', 'Auth\Controller\User'); // redirect
            $matches->setParam('action', 'accessdenied');

            return;
        }

}

/// rest of the code here

}