Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I refresh the session during a POST request?

I'm building an Ajax app in Symfony2 where a user logs in, and then from that point forward everything is handled by POST requests. After defining the session lifetime to 5 minutes in config.yml, I run into the problem of the user's session being invalidated after 5 minutes even though they're making POST requests. Each time a request is made, I'd like to reset the countdown before the session invalidates, but I'm not sure how to do this efficiently.

The method I'm currently thinking about is writing a listener for the kernel.request event, checking if the request method is POST, and manipulating the session class. I haven't done this yet, but it doesn't seem like a clean solution, since the listener needs to fire everytime a request is made.

Here's my session config:

session:  
    default_locale: %locale%  
    auto_start:     true  
    lifetime:       300  

Any ideas? Is the solution plain PHP and not Symfony?

like image 244
Steven Mercatante Avatar asked Dec 23 '11 00:12

Steven Mercatante


2 Answers

The lifetime of a session is it's maximum age. This is controlled via a cookie which has already been created and is not refreshed any longer from the server side (because the session is already established). You can just update this cookie manually and I think this will make it with symfony2.

The probably easiest is to regenerate the session id w/o destroying the session:

$this->get('session')->migrate();

This should trigger an update on the session cookie.

Probably related questions:

  • symfony2 session live time
  • Programmatically Set Session Lifetime in Symfony2
like image 179
hakre Avatar answered Nov 02 '22 19:11

hakre


To expound upon what is all ready here, here is a complete working example registered as a kernel request listener. For this example, I've hard-coded the timeout to 1,200 seconds (20 minutes). You can pass in the amount of time from your parameters.yml file (which is what I did in production):

#src\My\AppBundle\Resources\config\services.yml
kernel_request.listener:
    class:  My\AppBundle\EventListener\KernelRequestListener
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
    arguments: [@security.context, 1200]

And the class:

#Place in your src\My\AppBundle\EventListener folder
namespace My\AppBundle\EventListener {

    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\HttpKernel\HttpKernel;
    use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
    use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
    use Symfony\Component\Security\Core\SecurityContextInterface;

    class KernelRequestListener {

    /** @var int */
    private $maxIdleTime;

    /** @var SecurityContextInterface */
    private $securityContext;

    function __construct(SecurityContextInterface $securityContext, $maxIdleTime) {
        $this->securityContext = $securityContext;
        $this->maxIdleTime = $maxIdleTime;
    }

    public function onKernelRequest(GetResponseEvent $event) {
        if ($event->getRequestType() !== HttpKernel::MASTER_REQUEST) {
            // don't do anything if it's not the master request
            return;
        }

        $session = $event->getRequest()->getSession();
        $token = $this->securityContext->getToken();

        if ($session !== null && !($token instanceof AnonymousToken) && $token->isAuthenticated()) {
            $session->start();

            if ((time() - $session->getMetadataBag()->getLastUsed()) > $this->maxIdleTime) {
                throw new CredentialsExpiredException();
            }

            $session->migrate(false, $this->maxIdleTime);
        }
    }
}
like image 43
Dan Morphis Avatar answered Nov 02 '22 19:11

Dan Morphis