Как автоматически отключить пользователей после периода бездействия?
После долгих поисков в Интернете и ничего не находя, мне интересно, есть ли простой способ автоматического выхода пользователя, вошедшего в Symfony Security после неактивного периода. Например, я хочу, чтобы пользователь вышел из системы через 30 минут бездействия.
Я использую пользовательский провайдер, как это.
Но после входа пользователя в систему сеанс никогда не истекает. Даже если он закроет браузер и снова откроет его через несколько дней, сеанс все еще действителен.
Есть ли выход из системы этого пользователя автоматическим или даже ручным способом? Как я могу это сделать?
Ответы
Ответ 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