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

vla*_*irc 18 session symfony

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

我使用像这样的自定义用户提供程序.

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

无论如何,以自动方式甚至手动方式注销此用户?我怎样才能做到这一点?

com*_*oma 56

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

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

}
Run Code Online (Sandbox Code Playgroud)

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 }
Run Code Online (Sandbox Code Playgroud)

现在,您可以设置session_max_idle_timeparameters.yml 30*60 = 1800年秒(或任何你想要刚刚硬编码值):

参数 app/config/parameters.yml

parameters:
    ...
    session_max_idle_time: 1800
Run Code Online (Sandbox Code Playgroud)

  • @Brian,有趣的是,我一直在努力获得投票,而我现在已经有两年没有写过一行PHP了. (2认同)

HKa*_*lla 12

以下设置将注销不活动超过30分钟的用户.如果每29分钟发出一次请求,他们将永远不会被注销.请注意,在本地环境中测试并不容易,因为垃圾收集器只能从您的请求中调用,因此永远不会达到gc_maxlifetime!

#app/config/config.yml
session:
    cookie_lifetime: 86400
    gc_maxlifetime: 1800
Run Code Online (Sandbox Code Playgroud)

如果打开更多浏览器/会话并使用以下配置,则可以对此进行测试:

#app/config/config.yml
session:
    cookie_lifetime: 86400
    gc_maxlifetime: 1800
    gc_probability: 1
    gc_divisor: 1
Run Code Online (Sandbox Code Playgroud)

希望有所帮助!

请注意,添加:

 session:
    gc_probability: 1
    gc_divisor: 1
Run Code Online (Sandbox Code Playgroud)

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

  • 注意:这只会在到期时间后在第二个请求中注销用户,因为gc似乎是在请求后调用的.如果你有一个高流量的网站,这将是好的,但对于低,它不是真的很好. (2认同)

Ele*_*ero 7

如果有人想在 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)));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

参数app/config/service.yaml

parameters:
    ...
    session_max_idle_time: 600 // set to whatever value you want in seconds
Run Code Online (Sandbox Code Playgroud)

内核事件监听器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 }]
Run Code Online (Sandbox Code Playgroud)