内存缓存中的 ZF2 身份验证会话存储


ZF2 authentication session storage in memcached

在我们的内部网应用程序中,我们使用SSO(单点登录)登录,而客户端和身份验证源应用程序上的会话都存储在memcached中。

会话设置为生存 12 小时,然后垃圾回收器可能会将其视为删除。这两个应用程序都是使用 ZF2 编写的。

不幸的是,问题是,在一段时间后(我没有确切的值),浏览器会丢失会话,从而导致重定向到身份验证源,会话仍然处于活动状态,因此用户被重定向回客户端并刷新浏览器会话。如果用户没有未保存的工作,这没什么大不了的,因为这两个重定向发生在 1 秒内,用户甚至可能不会注意到它们。

但是,当用户有未保存的工作时,这确实是一件大事,甚至尝试保存它都会导致重定向并且工作消失。

以下是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);
                },
                // ...
        );
    }
}

身份验证服务定义为

            'authService' => function ($sm) {
                $authService = new 'Zend'Authentication'AuthenticationService;
                $authService->setStorage(new 'Zend'Authentication'Storage'Session('user_login'));
                return $authService;
            },
  • 会话存储使用相同的 Memcached 会话管理器。

然后在应用程序内的任何地方都需要检索或设置会话值,我只是使用这样的'Zend'Session'Container

$sessionContainer = new 'Zend'Session'Container('ClientXYZ');
$sessionContainer['key1'] = $val1;
// or
$val2 = $sessionContainer['key2'];

在任何操作中使用包含来自身份验证源的 PHPSESSID 的会话中的令牌为活动会话请求 SSO。在这个问题中描述是相当复杂的。

此外,身份验证服务还使用相同的设置将用户身份(带有ACL角色)存储在memcached会话中。显然,这是现在引起混乱的地方。显然,身份验证服务的会话存储过早超时,导致 ACL 检索没有用户身份进行检查,从而进入 SSO 注销序列(但由于用户没有真正注销,SSO 如上所述将用户重定向回去)。

不确定我应该(以及我可以)在这里分享多少代码,也许你会立即引导我找到解决方案,或者只是问我一些问题。经过数小时的调试并试图确定问题,我现在非常无助。

我在某处读到,一旦会话cookie的大小达到1MB,memcached就会清除内存 - 可能是这种情况吗?对于用户身份,我们只保存一般用户信息和角色数组,我猜这可能最大为几 kb......

编辑1:为了消除所有猜测并节省您的时间,这里有一些事实(要注意):

  • 仅使用内存缓存
  • Cookie仅用于在浏览器和服务器之间传输PHPSESSID,它的值是存储数据的memcached中内存块的键
  • 客户端和 SSO 身份验证应用程序在一台服务器上运行(无论是集成、暂存还是实时环境,仍然只有一台服务器)
  • 客户端应用程序上的会话随机关闭,导致它重定向到 SSO 身份验证应用程序,但此处会话仍处于活动状态,因此用户被重定向回客户端应用程序,该客户端应用程序获得新会话并且用户保持登录状态
  • 这应该会驳回有关 memcached 被擦除或重新启动的讨论
  • 此外,对telneted memcached的观察直接显示,两个数据块(用于客户端和身份验证应用程序)几乎同时使用相同的TTL建立

我将在 PHP 中实现一些die,在 JS 部分中实现一些 return s,以捕捉会话被认为消失的时刻,并进一步检查浏览器 cookie、memcached 数据等,并将更新您(除非有人提供解释和解决方案)。

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);
}

这是我用来为 X 用户创建 cookie 的功能。Cookie 的有效期为 3 小时,无论是否有重定向或用户是否关闭了浏览器。它仍然在那里。只需从 Module.php 在 onBootstrap() 方法中调用此函数即可。

在日志记录时,我使用 ZF2 身份验证服务和容器来存储和检索用户数据。

我建议您安装这些模块以便于调试。https://github.com/zendframework/ZendDeveloperToolshttps://github.com/samsonasik/SanSessionToolbar/

Memcached & gc_maxlifetime

当使用memcached作为session.save_handler时,会话的垃圾回收将不会完成

由于 Memcached 使用 TTL(生存时间)值,因此不需要垃圾回收。存活时间不足以达到 TTL 年龄的条目将被视为"新鲜"并将被使用。之后,它将被视为"过时",不再使用。最终 Memcached 将释放条目使用的内存,但这与 PHP 的会话垃圾收集无关。

实际上,在这种情况下实际使用的唯一session.gc_设置是 session.gc_maxlifetime ,它将作为 TTL 传递给 Memcached。

简而言之:垃圾收集在您的情况下不是问题。

Memcached & Cronjobs

当您使用 Memcached 作为会话的存储时,操作系统提供的任何手动清理磁盘上的会话文件夹的 cronjobs (如 Ubuntu 所做的)都不会产生任何影响。Memcached 是内存存储,而不是磁盘存储。

简而言之:像这样的 cronjobs 在您的情况下不是问题。

应用程序问题,而不是 SSO

您声明 SSO 服务器/颁发机构与 SSO 客户端(应用程序本身)位于同一台机器上,使用相同的 Web 服务器/PHP 配置,并且使用相同的 Memcached 实例。

这使我相信我们必须搜索如何在应用程序中完成会话管理,因为这是 SSO 权限和客户端之间的唯一区别。换句话说:我们需要深入研究Zend''Session。

免责声明:我专业地开发过几个 Zend Framework 1 应用程序,但没有开发任何 Zend Framework 2 应用程序。所以我在这里盲目飞行:)

配置

我在您的配置中注意到的一件事是您已将cookie_lifetime设置为 0 .这实际上意味着"直到浏览器关闭"。这在设置为 12 小时的情况下remember_me_seconds没有意义,因为很多人会在那之前关闭他们的浏览器。

我建议你把cookie_lifetime也设置为 12 小时。

另请注意,仅当实际使用"记住我"功能时,才会使用remember_me_seconds。换句话说:如果调用Zend'Session'SessionManager::rememberMe()

替代实施

看看你使用Memcached作为会话存储实现的方式,以及我能找到的关于这个主题的东西,我想说你做了一些不同于"首选方式"的事情。

有关此主题的大多数资源建议使用 Zend'Session'SaveHandler'Cache (doc, api) 作为保存处理程序,这使您能够使用 Zend'Cache'Storage'Adapter'Memcached (doc, api)。这使您可以更好地控制正在发生的事情,因为它不依赖于有限的会话保存处理程序memcached

我建议您尝试此实现。如果它不能立即解决您的问题,那么至少可以找到更多有关该主题的资源。恕我直言,您找到解决方案的机会会更好。

这个答案可能不会立即解决您的memcache问题的原因,但由于memcache的不可靠性质,我建议在某些持久存储中备份您的memcached数据。缓存数据将帮助您提高应用程序的性能,但它不是故障安全的。

也许您可以在AuthenticationService实例中进行回退(持久)存储。然后首先,您尝试从memcache中获取身份验证数据,如果未找到任何内容,请检查持久存储中是否有可用内容。

这至少可以解决所有意外的内存缓存丢失问题。