dousilie9522 2012-04-23 01:59
浏览 56

PHP MySQL session_set_save_handler竞争条件

I have written a simple session_set_save_handler using a MySQL innoDB table as the storage backend. MySQL is 5.5.

Code looks like so (very simplified, but hopefully illustrates the flow of code), I am using RedBean ORM for interacting with the database, but the commands should be clear in showing what was being written, read or deleted.:

class MySession{

    private $_database;  

    public function  __construct($database){
       //database object is injected using dependency injection then assigned to private var

       //Hook up handlers
       session_set_save_handler(
        array($this, "open"),
        array($this, "close"),
        array($this, "read"),
        array($this, "write"),
        array($this, "destroy"),
        array($this, "garbageCollection"));
       }

       //Register the shutdown function
       register_shutdown_function('session_write_close'); 

       //start
       session_start();

       //Regenerate session id
       //(NOTE: in production code, the id is regenerated every 10 minutes,
       //but regenerating allows us to trigger the race condition for demonstration.
       session_regenerate_id(TRUE);

    public function open($savePath, $sessionName){
        $this->_database->exec('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
        $this->_database->begin(); //Begin Transaction
        return true;
    }   

    public function close(){
        $this->_database->commit(); //Commit the transaction
        return true;
    }

    public function read($id){

        $this->_database->exec('SELECT * FROM session WHERE identifier = ? LOCK IN SHARE MODE', array($id));
        $session = $this->_database->findOne('session', 'identifier = ?', array($id));
        return $session->data;
    }

    public function write($id, $sessionData){

        $this->_database->exec('SELECT * FROM session WHERE identifier = ? FOR UPDATE', array($id));
        $session = $this->_database->findOne('session', 'identifier = ?', array($id));

        //We need to create a new session
        if ($session == NULL){
            $session = $this->_database->dispense('session');
        }

        $this->_database->store($session);
        return TRUE;
    }

    public function destroy($id){
        $this->_database->exec('SELECT * FROM session WHERE identifier = ? FOR UPDATE', array($id));
        $session = $this->_database->findOne('session', 'identifier = ?', array($id));

        if ($session != NULL){
            $this->_database->trash($session);
        }
        return TRUE;
    }

    public function garbageCollection($maxlifetime){
        $old = ... //Calculate the time for expired sessions

        $this->_database->exec("DELETE from session WHERE last_activity < ?", array($old));
        return TRUE;
    }
}

The problem is that even with the locking in place, if I send multiple requests to the application (for example, using AJAX), the race condition still occurs and the data stored in the session is lost. I have pinned it down to session_regenerate_id(TRUE), which deletes the previous session to being the cause of the issue. However, even with the row locking, the issue still occurs.

I have looked at this page, as well as some code from that author, but that uses MyISAM tables which implements table locking.

Can anyone shed some light as to why the locking is not making a difference and perhaps offer some solutions to this problem?

  • 写回答

1条回答 默认 最新

  • dqc19941228 2012-05-21 02:09
    关注

    I think your best option is to only regenerate the session ID when you know the request is a full-page request, rather than an AJAX request.

    if (empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
        session_regenerate_id(TRUE);
    }
    

    The above assumes the javascript library you're using sets the header.

    评论

报告相同问题?

悬赏问题

  • ¥15 vscode问题请教
  • ¥50 切换TabTip键盘的输入法
  • ¥15 可否在不同线程中调用封装数据库操作的类
  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM