How to log users off automatically after a period of inactivity?

后端 未结 9 1794
心在旅途
心在旅途 2020-11-27 03:21

After a lot of search in the web and find nothing, I wonder if there is an easy way to automatic logout the user logged through the Symfony Security after an inactive period

相关标签:
9条回答
  • 2020-11-27 04:02

    You have to implement it with a kernel listener, this is the way I solve it:

    Listener src/Comakai/MyBundle/Handler/SessionIdleHandler.php

    namespace Comakai\MyBundle\Handler;
    
    use Symfony\Component\HttpKernel\HttpKernelInterface;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\HttpFoundation\Session\SessionInterface;
    use Symfony\Component\Routing\RouterInterface;
    use Symfony\Component\HttpFoundation\RedirectResponse;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
    
    class SessionIdleHandler
    {
    
        protected $session;
        protected $securityToken;
        protected $router;
        protected $maxIdleTime;
    
        public function __construct(SessionInterface $session, TokenStorageInterface $securityToken, RouterInterface $router, $maxIdleTime = 0)
        {
            $this->session = $session;
            $this->securityToken = $securityToken;
            $this->router = $router;
            $this->maxIdleTime = $maxIdleTime;
        }
    
        public function onKernelRequest(GetResponseEvent $event)
        {
            if (HttpKernelInterface::MASTER_REQUEST != $event->getRequestType()) {
    
                return;
            }
    
            if ($this->maxIdleTime > 0) {
    
                $this->session->start();
                $lapse = time() - $this->session->getMetadataBag()->getLastUsed();
    
                if ($lapse > $this->maxIdleTime) {
    
                    $this->securityToken->setToken(null);
                    $this->session->getFlashBag()->set('info', 'You have been logged out due to inactivity.');
    
                    // Change the route if you are not using FOSUserBundle.
                    $event->setResponse(new RedirectResponse($this->router->generate('fos_user_security_login')));
                }
            }
        }
    
    }
    

    Config src/Comakai/MyBundle/Resources/config/services.yml (Comakai/MyBundle/DependencyInjection/MyBundleExtension.php)

    services:
        my.handler.session_idle:
            class: Comakai\MyBundle\Handler\SessionIdleHandler
            arguments: ["@session", "@security.context", "@router", %session_max_idle_time%]
            tags:
                - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    

    Now you can set the session_max_idle_time in parameters.yml to 30 * 60 = 1800 seconds (or just hardcode the value wherever you want):

    Parameters app/config/parameters.yml

    parameters:
        ...
        session_max_idle_time: 1800
    
    0 讨论(0)
  • 2020-11-27 04:02

    Here is my example with Symfony 4.

    Session was used instead of SessionInterface because this interface does not contain access to the getFlashBag() method.

    A redirection is performed on app_login and not on app_logout, otherwise the flashBag of the current session will be lost.

    $this->tokenStorage->setToken(); could be replaced by $this->tokenStorage->reset(); via the concrete class but the interface does not allow it.

    You could use this:

    <?php
    
    declare(strict_types=1);
    
    namespace App\EventListener;
    
    use Symfony\Component\HttpFoundation\RedirectResponse;
    use Symfony\Component\HttpFoundation\Session\Session;
    use Symfony\Component\HttpFoundation\Session\SessionInterface;
    use Symfony\Component\HttpKernel\Event\RequestEvent;
    use Symfony\Component\Routing\RouterInterface;
    use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
    use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
    use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
    
    class SessionIdleListener
    {
        /**
         * @var int
         */
        private $maxIdleTime;
    
        /**
         * @var Session
         */
        private $session;
    
        /**
         * @var TokenStorageInterface
         */
        private $tokenStorage;
    
        /**
         * @var RouterInterface
         */
        private $router;
    
        /**
         * @var AuthorizationCheckerInterface
         */
        private $checker;
    
        public function __construct(
            string $maxIdleTime,
            Session $session,
            TokenStorageInterface $tokenStorage,
            RouterInterface $router,
            AuthorizationCheckerInterface $checker
        ) {
            $this->maxIdleTime = (int) $maxIdleTime;
            $this->session = $session;
            $this->tokenStorage = $tokenStorage;
            $this->router = $router;
            $this->checker = $checker;
        }
    
        public function onKernelRequest(RequestEvent $event): void
        {
            if (!$event->isMasterRequest()
                || $this->maxIdleTime <= 0
                || $this->isAuthenticatedAnonymously()) {
                return;
            }
    
            $session = $this->session;
            $session->start();
    
            if ((time() - $session->getMetadataBag()->getLastUsed()) <= $this->maxIdleTime) {
                return;
            }
    
            $this->tokenStorage->setToken();
            $session->getFlashBag()->set('info', 'You have been logged out due to inactivity.');
    
            $event->setResponse(new RedirectResponse($this->router->generate('app_login')));
        }
    
        private function isAuthenticatedAnonymously(): bool
        {
            return !$this->tokenStorage->getToken()
                || !$this->checker->isGranted(AuthenticatedVoter::IS_AUTHENTICATED_FULLY);
        }
    }
    
    App\EventListener\SessionIdleListener:
        bind:
            $maxIdleTime: '%env(APP_SESSION_MAX_IDLE_TIME)%'
            $session: '@session'
        tags:
            - { name: kernel.event_listener, event: kernel.request }
    
    0 讨论(0)
  • 2020-11-27 04:06

    The following setting will log out users that are inactive for more than 30minutes. If a request is made every 29minutes, they will never be logged out. Please note that this is not easy to test in an local environment as the garbage collector is only called from your request thus the gc_maxlifetime is never reached!

    #app/config/config.yml
    session:
        cookie_lifetime: 86400
        gc_maxlifetime: 1800
    

    You can test this if you open more browsers/sessions and use the following config:

    #app/config/config.yml
    session:
        cookie_lifetime: 86400
        gc_maxlifetime: 1800
        gc_probability: 1
        gc_divisor: 1
    

    Hope that helps!

    Please note, adding:

     session:
        gc_probability: 1
        gc_divisor: 1
    

    Is only meant for testing the garbage collector on a local environment where there are no other requests that cause the garbage collector to remove your session. Making the garbage collector run on every request is not meant (or necessary) on a productive environment!

    0 讨论(0)
提交回复
热议问题