如何在一段时间不活动后自动关闭用户?

时间:2013-09-18 12:49:27

标签: session symfony

在网上进行了大量搜索后发现什么都没有,我想知道是否有一种简单的方法可以在非活动期后自动注销通过Symfony Security登录的用户。例如,我希望用户在30分钟不活动后退出。

我使用自定义用户提供程序,如this

但是在用户登录系统后,会话永不过期。即使他关闭浏览器并在几天后再次打开它,会话仍然有效。

无论如何都要以自动方式甚至手动方式注销这个用户? 我怎么能这样做?

9 个答案:

答案 0 :(得分:55)

你必须用内核监听器来实现它,这就是我解决它的方法:

监听器 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')));
            }
        }
    }

}

配置 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 }

现在,您可以将 parameters.yml 中的session_max_idle_time设置为30 * 60 = 1800 秒(或者只需将值硬编码到您想要的任何位置):< / p>

参数 app / config / parameters.yml

parameters:
    ...
    session_max_idle_time: 1800

答案 1 :(得分:11)

以下设置将注销超过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

仅用于在本地环境中测试垃圾收集器,其中没有其他请求导致垃圾收集器删除会话。在生产环境中,并不意味着(或必要)在每个请求上运行垃圾收集器!

答案 2 :(得分:4)

万一有人想在Symfony 4中实现这一点,我已经更新了@coma给出的答案,因为security.context已贬值,parameters.yml现在只是app / config / service.yaml的一部分,您可以注入建设者的其他变量。不过,基本上答案是相同的,只是针对Symfony 4进行了调整:

监听器 src / Security / 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

内核事件监听器 app / config / service.yaml

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

答案 3 :(得分:2)

与FOSUserbundle完美配合,谢谢。

我将此添加到内部条件以防止匿名用户被注销。

...

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

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

 ... do logout / redirect etc.

}

答案 4 :(得分:1)

在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:   ~

答案 5 :(得分:0)

怎么样:

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

答案 6 :(得分:0)

这是我使用Symfony 4的示例。

使用

Session代替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 }

答案 7 :(得分:-1)

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

答案 8 :(得分:-1)

一个简单的重定向,可以在使用布局中的树枝后立即注销

首先创建您的树枝扩展名

#App/Twig/LogoutAfterMomentExtension.php  

<?php


namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class LogoutAfterMomentExtension extends AbstractExtension
{
    public function getFunctions()
    {
        return [
            new TwigFunction('logoutAfter', [$this, 'logoutAfter']),
        ];
    }

    public function logoutAfter(int $seconds)
    {
        return header( "refresh:".$seconds.";url=/admin/logout" );
    }

}

调用模板中的函数

#templates/layout.html.twig

<body>

{{ logoutAfter(5) }} #it will logout after 5 seconds
...

</body>