【问题标题】:CodeIgniter unexpected session rotationCodeIgniter 意外的会话轮换
【发布时间】:2016-06-17 17:33:24
【问题描述】:

我有一个在 LAMP 上运行的 Codeigniter APP。我有一个奇怪的问题,Codeigniter 会以某种方式破坏会话并在 AJAX 调用期间生成一个新的会话 ID。问题是我已经将$config['sess_expiration'] 设置为0。在session.php 中,我更改了session_update 中的代码,

function sess_update()
{    
    if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
    {
        return;
    }    

    $old_sessid = $this->userdata['session_id'];  
    // Pass old session id to new session id directly
    $new_sessid = $old_sessid;

    $this->userdata['last_activity'] = $this->now;

    // _set_cookie() will handle this for us if we aren't using database sessions
    // by pushing all userdata to the cookie.
    $cookie_data = NULL;

    // Update the session ID and last_activity field in the DB if needed
    if ($this->sess_use_database === TRUE)
    {
        // set cookie explicitly to only have our session data
        $cookie_data = array();
        foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
        {
            $cookie_data[$val] = $this->userdata[$val];
        }

        $this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
    }

    // Write the cookie
    $this->_set_cookie($cookie_data);
}

无论是否是“要更新的会话”,会话 ID 都应该始终相同,因为我只是将旧会话 ID 传递给新会话 ID。以下是我的配置设置:

$config['sess_cookie_name']     = 'test';
$config['sess_expiration']      = 0;
$config['sess_expire_on_close'] = TRUE;
$config['sess_encrypt_cookie']  = TRUE;
$config['sess_use_database']    = TRUE;
$config['sess_table_name']      = 'ci_sessions_dev';
$config['sess_match_ip']        = FALSE;
$config['sess_match_useragent'] = FALSE;
$config['sess_time_to_update']  = 3; //the problem happens no matter this value is 3 or 30000

并且在日志中,当我登录后服务器将我踢出时,我可以看到日志:

DEBUG - 2016-03-06 13:48:17 --> A session cookie was not found.

有时会发生此问题,有时会整天工作。我在 FF、IE、Chrome 中遇到了这个问题。任何想法?提前谢谢你

【问题讨论】:

    标签: php codeigniter session


    【解决方案1】:

    找到问题了。

    我正在使用 Codeigniter v2。有一个名为“sess_read”的函数从数据库中读取会话信息。并且默认情况下,会话构造函数会执行 sess_gc 清除数据库中所有会话记录的可能性为 5%,这意味着如果有 20 个请求,其中一个会触发清除会话记录的功能。

    在触发“sess_gc”之后,下一个请求会先做一个sess_read,在这个“sess_read”中,它会检查数据库中的记录,但是找不到记录,因为sess_gc刚刚删除了所有的记录.然后系统会认为会话被破坏,它会做一个 sess_create 而不是 sess_update,这会搞砸整个事情。

    可怕的是,如果触发了sess_gc,系统将删除数据库中的所有会话记录,从而将所有用户踢出。不敢相信 Codeigniter 默认会这样做。

    我所做的是在函数中添加延迟,现在的代码是

    function _sess_gc()
    {
        if ($this->sess_use_database != TRUE)
        {
            return;
        }
    
        srand(time());
        if ((rand() % 100) < $this->gc_probability)
        {
            $expire = $this->now - $this->sess_expiration;
            $expire = $expire - 60*60*12*1; //Never delete session less than 12 hours old
            $this->CI->db->where("last_activity < {$expire}");
            $this->CI->db->delete($this->sess_table_name);
    
            log_message('debug', 'Session garbage collection performed.');
        }
    }
    

    有时即使我们在 gc 函数中添加延迟,我们仍然会遇到在多次 AJAX 调用期间重新创建会话的问题。然后这是一个完全不同的场景。这篇文章很好地解释了它。 http://www.hiretheworld.com/blog/tech-blog/codeigniter-session-race-conditions/comment-page-1#comment-4679 这篇文章建议我们可以在数据库的会话表中再增加一个列来保存会话轮换期间的旧会话ID。它极大地减少了在会话轮换期间同时有两个 AJAX 调用时发生错误的时间,但它并没有完全解决问题。例如,如果在会话轮换期间同时进行了三个 AJAX 调用,则会话仍然会失败。实际上,AJAX 调用背后的 db 查询性能在这里起着重要作用,如果打开查询/存储过程花费太多时间,则多个 AJAX 调用堆栈和挂起的可能性更高,可能性更高会话将失败。

    【讨论】:

    • 更新了我的回答,希望对你有帮助
    猜你喜欢
    • 2011-08-13
    • 1970-01-01
    • 2016-02-03
    • 1970-01-01
    • 1970-01-01
    • 2013-12-16
    • 2013-11-20
    • 2016-07-20
    • 1970-01-01
    相关资源
    最近更新 更多