Réaliser un système de Timeout pour Symfony2

[MAJ] Une petite précision sur ce que fait réellement cette fonctionnalité. Cela me permet de déconnecter un utilisateur de son interface si l’activité est interrompu pendant plus de 30 minutes. C’est un reset de session automatique.

Symfony ne fournissant pas cette fonction dans son framework, je vais vous montrer sa mise en place avec un listener. J’ai pour habitude d’avoir dans mes développements, un bundle Core qui me permet de centraliser les choses du mon projet.

Nous allons commencer par définir un paramètre dans notre arbre de configuration. Cela nous permettra de le renseigner ensuite dans notre fichier config.yml se trouvant dans le dossier app/config. Voici le code se trouvant dans le fichier CoreBundle/DependencyInjection/configuration.php

namespace Funstaff\CoreBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

class Configuration implements ConfigurationInterface
{
    /**
     * {@inheritDoc}
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('funstaff_core');

        $rootNode
            ->children()
                ->scalarNode('timeout')->defaultValue(3600)
                ->isRequired()->end()
            ->end();

        return $treeBuilder;
    }
}

Nous pouvons maintenant injecter notre paramètre dans le container. Pour cela, nous allons ajouter ce code dans le fichier FunstaffCoreExtension.php

namespace Funstaff\CoreBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

class FunstaffCoreExtension extends Extension
{
    /**
     * {@inheritDoc}
     */
    public function load(array $configs, ContainerBuilder $container)
    {
    	$configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\XmlFileLoader($container,
                         new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.xml');

        $container->setParameter('core.timeout', $config['timeout']);
    }
}

Dernière chose à faire avant d’implémenter notre listener, renseigner ce paramètre dans notre fichier config.yml. La valeur est exprimée en seconde.

funstaff_core:
    timeout:    1800 # 30 minutes

Pour respecter l’arborescence des dossiers, j’ai créé mon fichier RequestListener.php dans le path suivant: FunstaffCoreBundle/Request/Listener.

namespace Funstaff\CoreBundle\Request\Listener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class RequestListener implements EventSubscriberInterface
{
    protected $session;

    protected $securityContext;

    protected $timeout;

    /**
     * Construct
     * 
     * @param Session $session
     */
    public function __construct(Session $session,
                                SecurityContext $securityContext,
                                $timeout)
    {
        $this->session = $session;
        $this->securityContext = $securityContext;
        $this->timeout = $timeout;
    }

    /**
     * Get Subscribed Events
     * 
     * @return array event list
     */
    public static function getSubscribedEvents()
    {
        return array(
            'kernel.request' => 'onKernelRequest',
        );
    }

    /**
     * On Kernel Request
     */
    public function onKernelRequest(GetResponseEvent $event)
    {

        if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
            return;
        }

        $meta = $this->session->getMetadataBag();
        $lastused = $meta->getLastUsed();

        if (null !== $lastused && (time() - $lastused) > $this->timeout) {
            $this->securityContext->setToken(null);
            $this->session->invalidate();
        }
    }
}

Il nous reste une dernière chose à faire pour que cela fonctionne. Nous allons attacher notre listener à la request en définissant les éléments dans le fichier Resources/config/services.xml comme ceci:

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="request.listener.class">Funstaff\CoreBundle\Request\Listener\RequestListener</parameter>
    </parameters>

    <services>
        <service id="timeout.request.listener" scope="request">
            <tag name="kernel.event_subscriber"/>
            <argument type="service" id="session" />
            <argument type="service" id="security.context" />
            <argument>%core.timeout%</argument>
        </service>
    </services>
</container>

Je précise pour finir, que je travaille toujours en mode sécurité: anonymous = true. Il vous faudra peut-être modifier un peu le listener ci-dessus dans le cas contraire.

Voilà. Nous en avons terminé avec notre système de timeout. J’espère que cela vous servira dans vos prochains développement.

Share

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *