Подтвердить что ты не робот

Как автоматически отключить пользователей после периода бездействия?

После долгих поисков в Интернете и ничего не находя, мне интересно, есть ли простой способ автоматического выхода пользователя, вошедшего в Symfony Security после неактивного периода. Например, я хочу, чтобы пользователь вышел из системы через 30 минут бездействия.

Я использую пользовательский провайдер, как это.

Но после входа пользователя в систему сеанс никогда не истекает. Даже если он закроет браузер и снова откроет его через несколько дней, сеанс все еще действителен.

Есть ли выход из системы этого пользователя автоматическим или даже ручным способом? Как я могу это сделать?

4b9b3361

Ответ 1

Вы должны реализовать его с помощью прослушивателя ядра, так я его решаю:

Слушатель src/Comakai/MyBundle/Обработчик/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')));
            }
        }
    }

}

Конфигурация 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 }

Теперь вы можете установить session_max_idle_time в parameters.yml до 30 * 60 = 1800 секунд (или просто указать код, где бы вы ни хотели):

Параметры app/config/parameters.yml

parameters:
    ...
    session_max_idle_time: 1800

Ответ 2

Следующий параметр отключит пользователей, которые неактивны более 30 минут. Если запрос выполняется каждые 29 минут, они никогда не будут выходить из системы. Обратите внимание, что это нелегко проверить в локальной среде, поскольку сборщик мусора вызывается только из вашего запроса, поэтому gc_maxlifetime никогда не достигается!

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

Вы можете проверить это, если вы открываете больше браузеров/сеансов и используете следующую конфигурацию:

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

Надеюсь, что это поможет!

Обратите внимание, что добавление:

 session:
    gc_probability: 1
    gc_divisor: 1

Используется только для тестирования сборщика мусора в локальной среде, где нет других запросов, которые заставляют сборщика мусора удалить ваш сеанс. Создание сборщика мусора, выполняемого по каждому запросу, не означает (или необходимо) в продуктивной среде!

Ответ 3

На тот случай, если кто-то захочет реализовать это в Symfony 4, я обновил ответ, который дал @coma, поскольку security.context устарел, parameters.yml теперь является просто частью app/config/service.yaml, и вы можете просто вставить другие переменные для конструктора. Это в основном тот же ответ, просто настроенный для работы с Symfony 4:

Слушатель SRC/Безопасность/SessionIdleHandler.php (или в любом месте, он отображается в слушателе событий ниже)

<?php

namespace App\Security;

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($maxIdleTime, SessionInterface $session, TokenStorageInterface $securityToken, RouterInterface $router)
    {
        $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.');

                // logout is defined in security.yaml.  See 'Logging Out' section here:
                // https://symfony.com/doc/4.1/security.html
                $event->setResponse(new RedirectResponse($this->router->generate(logout)));
            }
        }
    }
}

Параметры app/config/service.yaml

parameters:
    ...
    session_max_idle_time: 600 // set to whatever value you want in seconds

Приложение- обработчик событий ядра/config/service.yaml

services:
    ...
    App.Handler.SessionIdle:
        class: App\Security\SessionIdleHandler
        arguments: ['%session_max_idle_time%']
        tags: [{ name: kernel.event_listener, event: kernel.request }]

Ответ 4

Прекрасно работает с FOSUserbundle, спасибо.

Я добавил это во внутреннее условие, чтобы предотвратить анонимный пользователь, чтобы он вышел из системы.

...

$isFullyAuthenticated = $this->securityContext->isGranted('IS_AUTHENTICATED_FULLY');

if ($lapse > $this->maxIdleTime && $isFullyAuthenticated == true) {

 ... do logout / redirect etc.

}

Ответ 5

В Symfony 2.4 следующие работы отлично подходят для меня в течение 1 часа:

framework:
    #esi:             ~
    translator:      { fallback: %locale% }
    secret:          %secret%
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
        http_port: 80
        https_port: 443
    form:            ~
    csrf_protection: ~
    validation:      { enable_annotations: true }
    templating:
        engines: ['twig']
        #assets_version: SomeVersionScheme
    default_locale:  "%locale%"
    trusted_proxies: ~
    session:         
        cookie_lifetime:       3600
    fragments:       ~
    trusted_hosts:   ~

Ответ 6

Как насчет:

#app/config/config.yml
framework:
    session:
        cookie_lifetime:       1800

Ответ 7

Вот мой пример с Symfony 4.

Сессия была использована вместо SessionInterface, потому что этот интерфейс не содержит доступа к getFlashBag().

Перенаправление выполняется на app_login а не на app_logout, в противном случае flashBag текущего сеанса будет потерян.

$this->tokenStorage->setToken(); может быть заменено на $this->tokenStorage->reset(); через конкретный класс, но интерфейс не позволяет этого.

Вы можете использовать это:

<?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 }

Ответ 8

Время жизни cookie не подходит, потому что это можно манипулировать клиентом, поэтому мы должны выполнить истечение срока действия на стороне сервера. Самый простой способ - реализовать это путем сбора мусора, который выполняется достаточно часто. Время cookie_lifetime будет установлено относительно высокое значение, и сбор мусора gc_maxlifetime будет настроен на уничтожение сеансов на любом желаемый период простоя.

framework:
    #esi:             ~
    #translator:      { fallback: "%locale%" }
    secret:          "%secret%"
    router:
        resource: "%kernel.root_dir%/config/routing.yml"
        strict_requirements: ~
    form:            ~
    csrf_protection: ~
    validation:      { enable_annotations: true }
    templating:
        engines: ['twig']
        #assets_version: SomeVersionScheme
    default_locale:  "%locale%"
    trusted_hosts:   ~
    trusted_proxies: ~
    session:
        # handler_id set to null will use default session handler from php.ini
        #handler_id:  ~
        cookie_lifetime: 9999
        gc_maxlifetime: 900
        gc_probability: 1
        gc_divisor: 2
    fragments:       ~
    http_method_override: true