php - Almacenamiento de la sesión de autenticación ZF2 en memcached
session authentication (3)
Memcached y gc_maxlifetime
Al usar memcached
como session.save_handler
, la recolección de basura de la sesión no se realizará.
Debido a que Memcached funciona con un valor TTL (tiempo de vida), la recolección de basura no es necesaria. Una entrada que no ha vivido lo suficiente para alcanzar la edad TTL se considerará "nueva" y se utilizará. Después de eso, se considerará "obsoleto" y no se usará más. Finalmente Memcached liberará la memoria utilizada por la entrada, pero esto no tiene nada que ver con la recolección de basura de la sesión de PHP.
De hecho, la única configuración de session.gc_
que se usa realmente en este caso es session.gc_maxlifetime
, que se pasará como TTL a Memcached.
En resumen: la recolección de basura no es un problema en su caso.
Memcached y Cronjobs
Como está utilizando Memcached como almacenamiento para sus sesiones, cualquier cronjobs proporcionado por el sistema operativo que borre manualmente las carpetas de sesión en el disco (como Ubuntu lo hace) no tendrá ningún efecto . Memcached es almacenamiento de memoria, no almacenamiento de disco.
En resumen: cronjobs como este no son un problema en su caso.
Problema de la aplicación, no de SSO
Usted declara que el servidor / autoridad SSO está en la misma máquina que el cliente SSO (la aplicación misma), está utilizando la misma configuración de servidor web / PHP y está utilizando la misma instancia de Memcached.
Esto me lleva a pensar que tenemos que buscar cómo se realiza la gestión de sesiones en la aplicación, ya que esa es la única diferencia entre la autoridad de SSO y el cliente. En otras palabras: tenemos que sumergirnos en Zend / Session.
Descargo de responsabilidad: he trabajado profesionalmente en varias aplicaciones de Zend Framework 1, pero no en ninguna de las aplicaciones de Zend Framework 2. Así que estoy volando a ciegas aquí :)
Configuración
Una cosa que noto en su configuración es que ha establecido cookie_lifetime
en 0
. Esto realmente significa "hasta que el navegador se cierre". Esto realmente no tiene sentido junto con remember_me_seconds
establecido en 12 horas, porque mucha gente habrá cerrado su navegador antes de esa hora.
Le sugiero que establezca cookie_lifetime
a 12 horas también.
También tenga en cuenta que remember_me_seconds
solo se usa cuando la funcionalidad Remember Me se usa realmente. En otras palabras: si se llama a Zend/Session/SessionManager::rememberMe()
.
Implementación alternativa
Mirando la forma en que ha implementado el uso de Memcached como almacenamiento de sesión, y lo que puedo encontrar sobre el tema, diría que ha hecho algo diferente de lo que parece ser "la forma preferida".
La mayoría de los recursos sobre este tema aconsejan utilizar Zend/Session/SaveHandler/Cache
( doc , api ) como manejador de guardado, que le da la capacidad de usar Zend/Cache/Storage/Adapter/Memcached
( doc , api ). Esto le da mucho más control sobre lo que está sucediendo, porque no confía en el limitador de sesión-guardar-controlador limitado a memcached
.
Sugiero que pruebes esta implementación. Si no resuelve el problema de inmediato, hay al menos muchos más recursos para encontrar sobre el tema. Tus posibilidades de encontrar una solución serán mejores en mi humilde opinión.
En nuestra (s) aplicación (es) de intranet, utilizamos el inicio de sesión único (SSO) (inicio de sesión único), mientras que las sesiones en las aplicaciones de cliente y de origen de autenticación se almacenan en memcached.
Las sesiones se configuran para vivir durante 12 horas antes de que el recolector de basura las considere como eliminadas. Ambas aplicaciones están escritas usando ZF2.
Desafortunadamente, el problema es que después de cierto período de tiempo (no tengo el valor exacto) el navegador pierde la sesión que causa la redirección al origen de autenticación, donde la sesión todavía está viva, por lo que el usuario es redirigido nuevamente al cliente y el la sesión del navegador se actualiza. Esto no es gran cosa si el usuario no tiene trabajo no guardado, ya que estos dos redireccionamientos suceden en 1 segundo y el usuario incluso puede no notarlos.
Pero realmente es un gran problema cuando el usuario tiene trabajo no guardado e incluso un intento de guardarlo genera redireccionamientos y el trabajo se ha ido.
Aquí está la configuración de la sesión en Bootstrap.php
:
class Module
{
public function onBootstrap(MvcEvent $e)
{
// ...
$serviceManager = $e->getApplication()->getServiceManager();
$sessionManager = $serviceManager->get(''session_manager_memcached'');
$sessionManager->start();
Container::setDefaultManager($sessionManager);
// ...
}
public function getServiceConfig()
{
return array(
''factories'' => array(
// ...
''session_manager_memcached'' => function ($sm) {
$systemConfig = $sm->get(''config'');
$config = new SessionConfig;
$config->setOptions(array(
''phpSaveHandler'' => ''memcache'',
''savePath'' => ''tcp://localhost:11211?timeout=1&retry_interval=15&persistent=1'',
''cookie_httponly'' => true,
''use_only_cookies'' => true,
''cookie_lifetime'' => 0,
''gc_maxlifetime'' => 43200, // 12h
''remember_me_seconds'' => 43200 // 12h
));
return new SessionManager($config);
},
// ...
);
}
}
El servicio de autenticación se define como
''authService'' => function ($sm) {
$authService = new /Zend/Authentication/AuthenticationService;
$authService->setStorage(new /Zend/Authentication/Storage/Session(''user_login''));
return $authService;
},
- el almacenamiento de la sesión usa el mismo administrador de sesión memcached.
Luego, en cualquier lugar dentro de la aplicación, se necesita recuperar o establecer un valor de sesión. Solo uso un /Zend/Session/Container
como este:
$sessionContainer = new /Zend/Session/Container(''ClientXYZ'');
$sessionContainer[''key1''] = $val1;
// or
$val2 = $sessionContainer[''key2''];
El SSO se solicita para la sesión activa en cualquier acción utilizando el token de la sesión que contiene PHPSESSID del origen de autenticación. Es bastante complicado de describir aquí dentro de esta pregunta.
Además, un servicio de autenticación almacena una identidad de usuario (con roles para ACL) también en la sesión de memcached, utilizando la misma configuración. Obviamente, este es ahora el lugar que causa confusión. Aparentemente, el almacenamiento de la sesión del servicio de autenticación expira prematuramente, lo que hace que la ACL no recupere ninguna identidad de usuario para controlar la secuencia de finalización de sesión de SSO (pero como el usuario realmente no se desconectó, SSO redirige al usuario como se describe anteriormente).
No estoy seguro de cuánto código debería (y puedo) compartir aquí, tal vez me lleven a la solución de inmediato o simplemente haciéndome algunas preguntas. Estoy completamente indefenso en este momento después de muchas horas de depuración e intento identificar el problema.
En algún lugar he leído que memcached borra la memoria una vez que la cookie de sesión obtiene 1 MB de tamaño, ¿puede ser este el caso? Para la identidad del usuario guardamos solo la información general del usuario y la matriz de funciones, supongo que esto podría ser máximo. hasta algunos kb de tamaño ...
EDIT 1: Para descartar todas las conjeturas y ahorrar su tiempo, aquí algunos datos (a tener en cuenta):
- solo se usa memcached
- las cookies solo sirven para transportar el
PHPSESSID
entre el navegador y el servidor y su valor es la clave para el fragmento de memoria en memcached donde se almacenan los datos - las aplicaciones de autenticación de cliente y SSO se ejecutan en un servidor (ya sea integración, almacenamiento intermedio o en vivo, solo un servidor)
- la sesión en la aplicación del cliente se apaga de forma aleatoria y hace que redireccione a la aplicación de autenticación SSO, pero aquí la sesión todavía está activa, por lo que el usuario se redirige a la aplicación cliente que obtiene nueva sesión y el usuario permanece conectado
- esto debería descartar la discusión acerca de que memcached sea borrado o reiniciado
- También la observación en memcached telneted muestra directamente que ambos fragmentos de datos (para aplicaciones de cliente y de autenticación) se establecen casi al mismo tiempo con el mismo ttl
Voy a implementar algunos die
en PHP y return
s en partes JS para ver el momento en que se considera que la sesión se fue e inspeccionar más la cookie del navegador, los datos de memoria, etc. y te actualizaré (a menos que alguien venga con explicación y solución )
Es posible que esta respuesta no aborde de inmediato la causa de su problema de Memcache, pero debido a la naturaleza poco confiable de Memcache, sugeriría hacer una copia de seguridad de sus datos almacenados en memoria permanente en algún lugar de almacenamiento persistente. Memcaching sus datos le ayudará a mejorar el rendimiento de su aplicación, pero no es a prueba de fallas.
Tal vez puedas hacer un almacenamiento alternativo (persistente) en tu instancia de AuthenticationService
. Luego, primero intenta obtener sus datos de autenticación de su Memcache y si no encuentra nada, verifique si hay algo disponible en su almacenamiento permanente.
Esto al menos resolverá todos los problemas inesperados de pérdida de memcache.
public function initSession()
{
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions([
''cookie_lifetime'' => 7200, //2hrs
''remember_me_seconds'' => 7200, //2hrs This is also set in the login controller
''use_cookies'' => true,
''cache_expire'' => 180, //3hrs
''cookie_path'' => "/",
''cookie_secure'' => Functions::isSSL(),
''cookie_httponly'' => true,
''name'' => ''cookie name'',
]);
$sessionManager = new SessionManager($sessionConfig);
// $memCached = new StorageFactory::factory(array(
// ''adapter'' => array(
// ''name'' =>''memcached'',
// ''lifetime'' => 7200,
// ''options'' => array(
// ''servers'' => array(
// array(
// ''127.0.0.1'',11211
// ),
// ),
// ''namespace'' => ''MYMEMCACHEDNAMESPACE'',
// ''liboptions'' => array(
// ''COMPRESSION'' => true,
// ''binary_protocol'' => true,
// ''no_block'' => true,
// ''connect_timeout'' => 100
// )
// ),
// ),
// ));
// $saveHandler = new Cache($memCached);
// $sessionManager->setSaveHandler($saveHandler);
$sessionManager->start();
return Container::setDefaultManager($sessionManager);
}
Esta es la función que uso para crear una cookie para el usuario X. La cookie vive durante 3 horas, sin importar si hay redirecciones o si el usuario ha cerrado el navegador. Todavía está allí. Simplemente llame a esta función en su método onBootstrap () desde Module.php.
Al iniciar sesión, utilizo el ZF2 AuthenticationService y el contenedor para almacenar y recuperar los datos del usuario.
Le sugiero que instale estos módulos para una depuración más sencilla. https://github.com/zendframework/ZendDeveloperTools https://github.com/samsonasik/SanSessionToolbar/