CodeIgniter框架自带Session失效的问题

Eddy 发布于2013-10-31 16:26:47 分类: 技术心得 已浏览loading 网友评论2条 我要评论

CodeIgniter框架内建了Session类,利用cookie并结合可选的数据库保存来实现Session的功能,具体可参考官方文档:Session类


最近在使用过程中,发现Session会莫名其妙的丢失,于是花了点时间看了下CI的源码,找到了问题所在,不知算不算CI的一个bug?:)


我的配置是使用数据库保存session信息:



$config['sess_expire_on_close']    = TRUE;

$config['sess_use_database'] = TRUE;



CI框架在每次请求时会读取cookie,不存在则创建一个新的,存在则更新该cookie。这个从Session类的构造函数中可以看到:



        // Run the Session routine. If a session doesn't exist we'll

        // create a new one.  If it does, we'll update it.

        if ( ! $this->sess_read())

        {

            $this->sess_create();

        }

        else

        {

            $this->sess_update();

        }



问题就出在这个更新和读取的同步上。我们先看看sess_read方法,完整代码就不贴了,如果配置了$config['sess_use_database'] = TRUE,则每次会执行这么一段代码:



        if ($this->sess_use_database === TRUE)

        {

            $this->CI->db->where('session_id', $session['session_id']);


            if ($this->sess_match_ip == TRUE)

            {

                $this->CI->db->where('ip_address', $session['ip_address']);

            }


            if ($this->sess_match_useragent == TRUE)

            {

                $this->CI->db->where('user_agent', $session['user_agent']);

            }


            $query = $this->CI->db->get($this->sess_table_name);


            // No result?  Kill it!

            if ($query->num_rows() == 0)

            {

                $this->sess_destroy();

                return FALSE;

            }


            //......省略


        }



CI会根据具体配置信息去数据库匹配是否存在session记录,如果没有找到,则sess_destroy(),sess_destroy()会销毁当前会话Session信息(清空cookie)。


我们再看看sess_update方法,这个方法会根据$config['sess_time_to_update']配置的时间间隔值,定时更新数据库中的session信息(换个新sessionId),同时设置cookie。



    function sess_update()

    {

        // We only update the session every five minutes by default

        if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)

        {

            return;

        }


        // Save the old session id so we know which record to

        // update in the database if we need it

        $old_sessid = $this->userdata['session_id'];

        $new_sessid = '';

        while (strlen($new_sessid) < 32)

        {

            $new_sessid .= mt_rand(0, mt_getrandmax());

        }


        // To make the session ID even more secure we'll combine it with the user's IP

        $new_sessid .= $this->CI->input->ip_address();


        // Turn it into a hash

        $new_sessid = md5(uniqid($new_sessid, TRUE));


        // Update the session data in the session data array

        $this->userdata['session_id'] = $new_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);

    }



两个关键的方法都看完了,那为何会出现session丢失呢?


我的理解是,存在这样一种极端情况:A请求到来,这时发现需要更新数据库中的session信息,于是去update数据库更新相关值。与此同时B请求到来,此时B携带的还是cookie中保存的老的sessionId(A请求还未执行完成,只执行到数据库更新,还未设置新的cookie),B这时再去匹配数据库中的session会失败(因为A中已经更新了),于是会调用sess_destroy销毁session信息(把A请求中最后设置的新cookie也干掉了)。最终结果就是session丢失了。


解决办法就是:1、不启用数据库保存。$config['sess_use_database']    = FALSE;2、弄个标志位让更新操作全部完成才能执行下一个请求,否则阻塞挂起请求。

已经有(2)位网友发表了评论,你也评一评吧!
原创文章如转载,请注明:转载自Eddy Blog
原文地址:http://www.rrgod.com/technique/888.html     欢迎订阅Eddy Blog

关于 CodeIgniter  Session  失效  的相关文章

  1. 发表于2014-5-19 9:57:34

    也有发现同样的情况。看了你的话,现在已经把登陆给改成不存储数据库了。。郁闷了好久。谢谢你!
    唯一感觉更加郁闷的是,这么大的一个问题网上竟然很少有人讨论

  2. 发表于2014-10-27 17:33:17

    不错的文章,厉害!

记住我的信息,下次不用再输入 欢迎给Eddy Blog留言