13

I installed ZFCUser successfully. Now I wonder if there is a way to globally check for authentication.

As outlined in the wiki there are several ways to check for auth. They all work but do I have to put the check-if-clause really in every single action? All my sites should be only accessable when beeing logged in and if not, you should be rerouted to the login page.

Does anybody know if there's a central place where I can put this logic?

Ron
  • 22,128
  • 31
  • 108
  • 206

5 Answers5

26

To be honest, I don't think it is a good idea to block every page for a non-authenticated user. How would you access the login page?

That said, you must know the page being accessed, to make a whitelist of pages accessible for anonymous visitors. To start, I'd suggest to include the login page. You can check pages the easiest by using their route. So check the current matched route against the whitelist. If blocked, act upon. Otherwise, do nothing.

An example would be inside a Module.php from a module, for example your application:

namespace Application;

use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;

class Module
{
    protected $whitelist = array('zfcuser/login');

    public function onBootstrap($e)
    {
        $app = $e->getApplication();
        $em  = $app->getEventManager();
        $sm  = $app->getServiceManager();

        $list = $this->whitelist;
        $auth = $sm->get('zfcuser_auth_service');

        $em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($list, $auth) {
            $match = $e->getRouteMatch();

            // No route match, this is a 404
            if (!$match instanceof RouteMatch) {
                return;
            }

            // Route is whitelisted
            $name = $match->getMatchedRouteName();
            if (in_array($name, $list)) {
                return;
            }

            // User is authenticated
            if ($auth->hasIdentity()) {
                return;
            }

            // Redirect to the user login page, as an example
            $router   = $e->getRouter();
            $url      = $router->assemble(array(), array(
                'name' => 'zfcuser/login'
            ));

            $response = $e->getResponse();
            $response->getHeaders()->addHeaderLine('Location', $url);
            $response->setStatusCode(302);

            return $response;
        }, -100);
    }
}
Jurian Sluiman
  • 13,498
  • 3
  • 67
  • 99
  • Mmm, not sure... I wrote this out of my head, so try to enable php errors and set the error level to -1 (meaning, all errors) and see what's getting wrong here. Perhaps the router cannot be found, but as far as I know the router is inside the `$e` from the `onBootstrap` **and** in the `$e` from the callback inside the route event. If not, try to grasp the router from the `$e` inside the `onBoostrap()` method and import it just like the `$list` and `$auth`. – Jurian Sluiman Jan 03 '13 at 12:25
  • you need to alter `new Response` to `$e->getResponse()` then it works perfectly! – Ron Jan 03 '13 at 12:30
  • Thanks for working that out. I updated my answer accordingly for future reference :) – Jurian Sluiman Jan 03 '13 at 12:33
  • 1
    I have two questions about this. Only is need to put that code in one module.php for all modules? In my case i'm doing an ajax call to the server from login page to authenticate and i have to add to the whitelist the route "MyAction/procces"; Why? The code seems to work very well even with my custom authentication service (without ZFCUser) – user1011138 Jan 16 '13 at 10:09
  • 1
    If you have a new question, please hit the "Ask question" button at the top right of this site. Furthermore: yes this code runs on every call and you do not need to put it in every module class. If you need a more fine grained user/ACL mechanism, check out ZfcUser and BjyAuthorize. Only for very simple setups and illustration purposes above code is valid. – Jurian Sluiman Jan 16 '13 at 11:05
  • I had to replace if (!$match instanceof RouteMatch) with if (!$match instanceof \Zend\Mvc\Router\Http\RouteMatch) and every thing works fine... – cwhisperer Oct 08 '13 at 05:15
  • my friend it take application slow because event listener , do you have a better way to doing it ? – babak faghihian Nov 15 '15 at 08:46
0

On ZF 2.4.2 I do this in Module.php

class module {

protected $whitelist = array(
    'Application\Controller\Login'
);

public function onBootstrap(MvcEvent $e)
{

    $eventManager        = $e->getApplication()->getEventManager();
    $moduleRouteListener = new ModuleRouteListener();
    $moduleRouteListener->attach($eventManager);

    // add event
    $eventManager->attach('dispatch', array($this, 'checkLogin')); 

}

public function checkLogin($e)
{

    $auth   = $e->getApplication()->getServiceManager()->get("Zend\Authentication\AuthenticationService");
    $target = $e->getTarget();
    $match  = $e->getRouteMatch();

    $controller = $match->getParam('controller');

    if( !in_array($controller, $this->whitelist)){
        if( !$auth->hasIdentity() ){
            return $target->redirect()->toUrl('/login');
        }
    }

}

//other methods....
}
rafaelphp
  • 279
  • 2
  • 11
0

You can use ZF2 module BjyAuthorize to block/allow access to pages based on user roles such as guest, user etc using controller guard, route guard etc

Ankit Sharma
  • 5,191
  • 2
  • 23
  • 29
0

People,

Tip, dont forget the add the "use" to correct RouteMatch statement:

use Zend\Mvc\Router\Http\RouteMatch;

Here need this:

if (!$match instanceof RouteMatch)...

If you forget, the if above have inconstant

Giuseppe Lopes
  • 101
  • 1
  • 7
-3

Another option might be to create your own abstract controller superclass and implement the onDispatch() method like this:

public function onDispatch(MvcEvent $e) 
{
    // check authentication here

    return parent::onDispatch($e);
}

You can implement a whitelist there too :).

Ivan Novakov
  • 130
  • 1
  • 4