Skip to content

magneto/magento2#12362 Do not remove session instantly #14484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/code/Magento/Store/etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<use_http_x_forwarded_for>0</use_http_x_forwarded_for>
<use_http_user_agent>0</use_http_user_agent>
<use_frontend_sid>1</use_frontend_sid>
<old_session_access_delta>30</old_session_access_delta>
</session>
<browser_capabilities>
<cookies>1</cookies>
Expand Down
1 change: 1 addition & 0 deletions app/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
<preference for="Magento\Framework\Filter\Encrypt\AdapterInterface" type="Magento\Framework\Filter\Encrypt\Basic" />
<preference for="Magento\Framework\Cache\ConfigInterface" type="Magento\Framework\Cache\Config" />
<preference for="Magento\Framework\View\Asset\MergeStrategyInterface" type="Magento\Framework\View\Asset\MergeStrategy\Direct" />
<preference for="Magento\Framework\Session\Actualization\StorageInterface" type="Magento\Framework\Session\Actualization\Storage"/>
<preference for="Magento\Framework\App\ViewInterface" type="Magento\Framework\App\View" />
<preference for="Magento\Framework\Data\Collection\EntityFactoryInterface" type="Magento\Framework\Data\Collection\EntityFactory" />
<preference for="Magento\Framework\Translate\InlineInterface" type="Magento\Framework\Translate\Inline" />
Expand Down
119 changes: 119 additions & 0 deletions lib/internal/Magento/Framework/Session/Actualization/Storage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/**
* Session Actualization Storage
*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\Session\Actualization;

/**
* Actualization Storage
* Store required information for session actualization in conditions of unstable connection.
*/
class Storage extends \Magento\Framework\Session\Storage implements
\Magento\Framework\Session\Actualization\StorageInterface
{
/**
* Storage constructor.
*
* @param string $namespace
* @param array $data
*/
public function __construct($namespace = self::STORAGE_NAMESPACE, array $data = [])
{
parent::__construct($namespace, $data);
}

/**
* Get actual session id.
*
* @return string
*/
public function getNewSessionId()
{
return $this->getData(self::NEW_SESSION_ID);
}

/**
* Set actual session id.
*
* @param string $sessionId
* @return $this
*/
public function setNewSessionId($sessionId)
{
return $this->setData(self::NEW_SESSION_ID, $sessionId);
}

/**
* Check if session storage contains info about actual session.
*
* @return bool
*/
public function hasNewSessionId()
{
return $this->hasData(self::NEW_SESSION_ID);
}

/**
* Get old session id.
*
* @return string
*/
public function getOldSessionId()
{
return $this->getData(self::OLD_SESSION_ID);
}

/**
* Set old session id.
*
* @param string $sessionId
* @return $this
*/
public function setOldSessionId($sessionId)
{
return $this->setData(self::OLD_SESSION_ID, $sessionId);
}

/**
* Check if actual session contains id of old session.
*
* @return bool
*/
public function hasOldSessionId()
{
return $this->hasData(self::OLD_SESSION_ID);
}

/**
* Remove old session id.
*
* @return $this
*/
public function unsOldSessionId()
{
return $this->unsetData(self::OLD_SESSION_ID);
}

/**
* Get timestamp when session become deprecated.
*
* @return int
*/
public function getSessionOldTimestamp()
{
return $this->getData(self::SESSION_OLD_TIMESTAMP);
}

/**
* Set session as deprecated.
*
* @return $this
*/
public function setSessionOldTimestamp()
{
return $this->setData(self::SESSION_OLD_TIMESTAMP, time());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
/**
* Session Actualization Storage interface
*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\Session\Actualization;

/**
* Session Actualization Storage interface
*/
interface StorageInterface extends \Magento\Framework\Session\StorageInterface
{
const STORAGE_NAMESPACE = 'actualization';

const SESSION_OLD_TIMESTAMP = 'destroyed';

const NEW_SESSION_ID = 'new_session_id';

const OLD_SESSION_ID = 'old_session_id';

/**
* Get actual session id.
*
* @return string
*/
public function getNewSessionId();

/**
* Set actual session id.
*
* @param string $sessionId
* @return $this
*/
public function setNewSessionId($sessionId);

/**
* Check if session storage contains info about actual session.
*
* @return bool
*/
public function hasNewSessionId();

/**
* Get old session id.
*
* @return string
*/
public function getOldSessionId();

/**
* Set old session id.
*
* @param string $sessionId
* @return $this
*/
public function setOldSessionId($sessionId);

/**
* Check if actual session contains id of old session.
*
* @return bool
*/
public function hasOldSessionId();

/**
* Remove old session id.
*
* @return $this
*/
public function unsOldSessionId();

/**
* Get timestamp when session become deprecated.
*
* @return int
*/
public function getSessionOldTimestamp();

/**
* Set session as deprecated.
*
* @return $this
*/
public function setSessionOldTimestamp();
}
97 changes: 92 additions & 5 deletions lib/internal/Magento/Framework/Session/SessionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
namespace Magento\Framework\Session;

use Magento\Framework\App\ObjectManager;
use Magento\Framework\Session\Config\ConfigInterface;

/**
Expand Down Expand Up @@ -92,6 +93,11 @@ class SessionManager implements SessionManagerInterface
*/
private $appState;

/**
* @var \Magento\Framework\Session\Actualization\StorageInterface
*/
private $actualizationStorage;

/**
* @param \Magento\Framework\App\Request\Http $request
* @param SidResolverInterface $sidResolver
Expand All @@ -102,7 +108,9 @@ class SessionManager implements SessionManagerInterface
* @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
* @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
* @param \Magento\Framework\App\State $appState
* @param \Magento\Framework\Session\Actualization\StorageInterface $actualizationStorage
* @throws \Magento\Framework\Exception\SessionException
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\App\Request\Http $request,
Expand All @@ -113,7 +121,8 @@ public function __construct(
StorageInterface $storage,
\Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
\Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
\Magento\Framework\App\State $appState
\Magento\Framework\App\State $appState,
\Magento\Framework\Session\Actualization\StorageInterface $actualizationStorage = null
) {
$this->request = $request;
$this->sidResolver = $sidResolver;
Expand All @@ -124,7 +133,8 @@ public function __construct(
$this->cookieManager = $cookieManager;
$this->cookieMetadataFactory = $cookieMetadataFactory;
$this->appState = $appState;

$this->actualizationStorage = $actualizationStorage ?:
ObjectManager::getInstance()->get('Magento\Framework\Session\Actualization\StorageInterface');
// Enable session.use_only_cookies
ini_set('session.use_only_cookies', '1');
$this->start();
Expand Down Expand Up @@ -186,8 +196,7 @@ public function start()
$sid = $this->sidResolver->getSid($this);
// potential custom logic for session id (ex. switching between hosts)
$this->setSessionId($sid);
session_start();
$this->validator->validate($this);
$this->sessionStart();
$this->renewCookie($sid);

register_shutdown_function([$this, 'writeClose']);
Expand All @@ -200,6 +209,50 @@ public function start()
return $this;
}

/**
* Start session.
*
* @return void
* @throws \Magento\Framework\Exception\SessionException
* @SuppressWarnings(PHPMD.Superglobals)
*/
private function sessionStart()
{
session_start();
$this->validator->validate($this);
$this->actualizationStorage->init(isset($_SESSION) ? $_SESSION : []);

$forwarded = false;
while ($this->actualizationStorage->hasNewSessionId()) {
// Looks like we work with unstable network. Start more actual session.
$this->restartSession($this->actualizationStorage->getNewSessionId());
$this->validator->validate($this);
$forwarded = true;
}

if ($forwarded == false && $this->actualizationStorage->hasOldSessionId()) {
// New cookie was received. Proceed with old session delete.
$this->destroyOldSession();
}
}

/**
* Destroy old session.
*
* @return void
*/
private function destroyOldSession()
{
$currentSessionId = $this->getSessionId();
$oldSessionId = $this->actualizationStorage->getOldSessionId();
$this->actualizationStorage->unsOldSessionId();
$this->restartSession($oldSessionId);
if ($currentSessionId == $this->actualizationStorage->getNewSessionId()) {
session_destroy();
}
$this->restartSession($currentSessionId);
}

/**
* Renew session cookie to prolong session.
*
Expand Down Expand Up @@ -505,7 +558,7 @@ public function regenerateId()
}

if ($this->isSessionExists()) {
session_regenerate_id(true);
$this->regenerateSessionId();
} else {
session_start();
}
Expand All @@ -517,6 +570,40 @@ public function regenerateId()
return $this;
}

/**
* Regenerate session id, with saving relation between two sessions.
*
* @return void
*/
protected function regenerateSessionId()
{
$oldSessionId = session_id();
session_regenerate_id();
$this->actualizationStorage->setOldSessionId($oldSessionId);

$newSessionId = session_id();
$this->restartSession($oldSessionId);
$this->actualizationStorage->setSessionOldTimestamp();
$this->actualizationStorage->setNewSessionId($newSessionId);

$this->restartSession($newSessionId);
}

/**
* Restart the session with new ID.
*
* @param string $sid
* @return void
* @SuppressWarnings(PHPMD.Superglobals)
*/
protected function restartSession($sid)
{
session_commit();
$this->setSessionId($sid);
session_start();
$this->actualizationStorage->init(isset($_SESSION) ? $_SESSION : []);
}

/**
* Expire the session cookie for sub domains
*
Expand Down
Loading