Codeigniter会话与Ajax调用
我的CodeIgniter应用程序使用会话库并将数据保存到数据库。
我一直有一些问题,在某些Ajax调用后创build空白会话。
经过调查,似乎有两个同时发起的函数调用需要进行会话validation。 一个会失败,另一个会没事的。
我能够通过不让他们同时开火来解决这个问题。 但是我还是不明白为什么它失败了。 这是否与更新用户cookie和第二次调用无效的一次调用有关? 或者,也许在阅读数据库时,它以某种方式死亡?
我查了一下Session核心类,并没有find任何线索。
如果任何人遇到同样的问题,我将不知道如何debugging的build议或原因是什么。
谢谢!
编辑:
我原来说有408回报状态。 这是一个无关的案件。
这是并行触发MyVar.refresh()的函数:
function (event) { var self$ = this.a$; var uid = this.b$.val(); var tid = this.c$.val(); var jqxhr = $.post('/controller1/index',{'uid':uid,'tid':tid,'action':true},function(re) { if(re.message != 'success') { MyVar.alert('<span class="msg_error sprite"></span>' + re.error); MyVar.refresh(); } },'json'); MyVar.refresh(); return stopDefault(event); };
可能的解决scheme:
find这个: http : //codeigniter.com/forums/viewthread/102456/
显然,它不能很好地与阿贾克斯打。 一个解决scheme是禁止会话更新,如果它是一个Ajax调用; 唯一的问题是,我们的网站大多是用ajax构build的..
另外,只是把sess_time_to_update降到了非常频繁的地步,而且Ajax也很好。 也做了浏览器刷新,并没有超时。 不知道为什么如果会话ID已经改变了一个AJAX调用和浏览器的cookie从来没有更新。
尝试这个
<?php /** * ------------------------------------------------------------------------ * CI Session Class Extension for AJAX calls. * ------------------------------------------------------------------------ * * ====- Save as application/libraries/MY_Session.php -==== */ class MY_Session extends CI_Session { // -------------------------------------------------------------------- /** * sess_update() * * Do not update an existing session on ajax or xajax calls * * @access public * @return void */ public function sess_update() { $CI = get_instance(); if ( ! $CI->input->is_ajax_request()) { parent::sess_update(); } } } // ------------------------------------------------------------------------ /* End of file MY_Session.php */ /* Location: ./application/libraries/MY_Session.php */
问题出在会话类的sess_update函数中,在X秒后生成一个新的session_id。 每个页面都有一个session_id,如果session_id在ajax调用之前到期,那么调用将会失败。
在/ application / libraries /中创build一个名为MY_Session(或者你设置的任何前缀)的php文件,粘贴这个代码就行了。 这个函数将覆盖会话类中的sess_update函数,如果请求是由ajax创build的,则检查每个请求,然后跳过sess_update函数。
将一个sess_expiration设置为更高的值是一个糟糕的主意。 这是一项安全function,可以防止会话劫持
PD:我不太stream利的英文,如果你不明白的东西只是让我知道。
在合并到stable分支之前,解决scheme(最终!)将使用Areson的提交245bef5与数据库模式相结合:
CREATE TABLE IF NOT EXISTS `ci_sessions` ( session_id varchar(40) DEFAULT '0' NOT NULL, ip_address varchar(45) DEFAULT '0' NOT NULL, user_agent varchar(120) NOT NULL, last_activity int(10) unsigned DEFAULT 0 NOT NULL, user_data text NOT NULL, prevent_update int(10) DEFAULT NULL, PRIMARY KEY (session_id), KEY `last_activity_idx` (`last_activity`) );
欲了解更多信息,请阅读从上到下拉1283条评论 。
我们遇到了这个问题,这是由于config.php中的sess_time_to_update参数造成的。 CI使用它来更新会话ID到一个新的。 如果更改发生在ajax调用中,CI发送一个新的cookie来告诉浏览器新的会话ID。 不幸的是,浏览器似乎忽略了这个cookie,并保留旧的会话ID。
我们通过在configuration中将sess_time_to_update设置为sess_expiration来修复它。
$config['sess_time_to_update'] = $config['sess_expiration'];
在codeigniter版本2.1.3中,我也有这个问题,当我使用下面的configuration:
$config['sess_use_database'] = TRUE; $config['sess_time_to_update'] = 300;
我认为这与ajax请求无关,而是与codeigniter中的一个错误。
看起来,当你将会话存储在数据库中时,在300秒之后强制注销。 经过3个小时的search和分析,我发现代码中有一个明显的错误,并且还有一个不清楚的错误,我已经解决了这个错误,如下所示:
在application / libraries文件夹中创build一个新文件:MY_Session.php
将下面的代码添加到它:
<?php // fixed by sirderno 2013 if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class MY_Session extends CI_Session { public function __construct() { parent::__construct(); } /** * Update an existing session * * @access public * @return void */ public 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]; } $cookie_data['session_id'] = $new_sessid; // added to solve bug //added to solve bug if (!empty($this->userdata['user_data'])) $cookie_data['user_data'] = $this->userdata['user_data']; $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); } /** * Write the session cookie * * @access public * @return void */ public function _set_cookie($cookie_data = NULL) { if (is_null($cookie_data)) { $cookie_data = $this->userdata; } // Serialize the userdata for the cookie $cookie_data = $this->_serialize($cookie_data); if ($this->sess_encrypt_cookie == TRUE) { $cookie_data = $this->CI->encrypt->encode($cookie_data); } else { // if encryption is not used, we provide an md5 hash to prevent userside tampering $cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key); } $_COOKIE[ $this->sess_cookie_name ] = $cookie_data; // added to solve bug $expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time(); // Set the cookie setcookie( $this->sess_cookie_name, $cookie_data, $expire, $this->cookie_path, $this->cookie_domain, $this->cookie_secure ); } } ?>
明确的错误是,它没有在更新的cookie中存储“user_data”。 不清楚的bug是在更新新的会话ID后执行文件Session.php中的函数sess_read(),我不知道为什么会发生这种情况,因为我希望它在更新之前执行,而不是像它写在构造函数Session.php。 因此,sess_read()函数开始读取旧的cookie信息与旧的会话ID,并希望将其与数据库中的会话ID进行比较,但session_id更新后,它不再存在于数据库中,所以这会导致注销。
Session.php文件的函数sess_read中的这一行代码负责读取旧的cookie信息:
$session = $this->CI->input->cookie($this->sess_cookie_name);
所以在MY_Session.php的函数_set_cookie中,我添加了这一行代码来更新服务器的旧cookie信息:
$_COOKIE[ $this->sess_cookie_name ] = $cookie_data; // added to solve bug
有了这个修复,“sess_time_to_update”与“sess_use_database”结合应该可以正常工作。 这是一个简单而简单的错误修复。
当用ajax上传图片时,我遇到了完全相同的问题,并将sess_expiration
设置为:
$config['sess_expiration'] = time()+10000000;
它解决了我的问题。
好的解决scheme在这里。 做什么与sess_time_to_update等尝试下面的解决scheme
- https://degreesofzero.com/article/fixing-the-expiring-session-problem-in-codeigniter.html
- http://ellislab.com/forums/viewthread/138823/#725078
到解决scheme编号“1”。 我更多地更新一点脚本。 经过与CI的破解后,我得到了CI SESSIONS有两个原因让人失望的观点。 一个是不好的Ajax调用会话得到UPDATED和会话丢失; 其次是在ajax调用后,会影响到CI的SESSION库中的sess_destroy函数。 所以我做了一点“1” 解决scheme是
/*add this code to MY_Session.php*/ function sess_destroy() { // Do NOT update an existing session on AJAX calls. if (!$this->CI->input->is_ajax_request()) { return parent::sess_destroy(); } /* WHEN USER HIS/HER SELF DO A LOGOUT AND ALSO IF PROGRAMMER SET TO LOGOUT USING AJAX CALLS*/ $firsturlseg = $this->CI->security->xss_clean( $this->CI->uri->segment(1) ); $securlseg = $this->CI->security->xss_clean( $this->CI->uri->segment(2) ); if((string)$firsturlseg==(string)'put ur controller name which u are using for login' && (string)$securlseg==(string)'put url controler function for logout') { return parent::sess_destroy(); } }
希望你也帮助你
核心CI会话类处理会话似乎有一个缺陷。
find了一个像魅力一样工作的备用会话库。
CI备用会话库
我build议扩展核心CI_Session类而不是replace它。
为了扩展,在application/libraries
创build一个文件MY_Session.php
。 粘贴备用库的内容,将class CI_Session
replace为class MY_Session extends CI_Session
。
从_flashdata_mark()
, _flashdata_sweep()
, _get_time()
, _set_cookie()
, _serialize()
, _unserialize()
, _sess_gc()
函数中移除受保护的函数。
希望它有帮助。
似乎仍然有很多旧的CI版本在使用,我想添加我的两分钱,即使这个线程是旧的。 我花了几天的时间解决了代码点火器中AJAX调用的问题,我有一个解决scheme,涵盖了主要问题,虽然有些解决scheme不是很好。 我(仍)使用的CI版本是2.1.3
我的应用程序需要AJAX调用更新last_activity字段来维护一个有效的会话,所以我不能简单地放弃在AJAX调用更新会话。
sess_update和sess_read的错误检查在这个CI版本中是不充分的(我还没有调查更新的版本),并且很多问题从那里开始。
第一部分: sess_update()
多个AJAX调用会创build竞争条件,导致为稍后的调用而locking数据库。 如果我们试图运行一个更新查询但是数据库被locking了,我们得到一个错误,查询返回false,但是cookie仍然被更新为新的数据? 另外,我们不需要每个Ajax调用都有一个新的session_id。 我们只需要更新last_activity。 尝试这个:
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']; //Assume this is an AJAX call... keep the same session_id $new_sessid = $old_sessid; if( !$this->CI->input->is_ajax_request() ){ //Then create a new session id 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)); } // _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) { //TRY THE QUERY FIRST! //Multiple simultaneous AJAX calls will not be able to update because the Database will be locked. ( Race Conditions ) //Besides... We don't want to update the cookie if the database didn't update $query = $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))); if( $query ){ // Update the session data in the session data array $this->userdata['session_id'] = $new_sessid; $this->userdata['last_activity'] = $this->now; // 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]; } // Write the cookie $this->_set_cookie($cookie_data); }else{ //do nothing... we don't care, we still have an active retreivable session and the update didn't work //debug: error_log( "ERROR::" . $this->CI->db->_error_message() ); //Shows locked session database } }else{ // Update the session data in the session data array $this->userdata['session_id'] = $new_sessid; $this->userdata['last_activity'] = $this->now; // Write the cookie $this->_set_cookie($cookie_data); } }
第2部分: sess_read()
在这里非常类似的问题…在查询期间,数据库有时被locking。 除了这次我们不能忽视这些错误。 我们正在尝试阅读会话,看看它是否存在…所以如果我们得到一个locking的数据库错误,我们可以检查错误,然后再试一次 (如果需要的话多次)。 在我的testing中,我从来没有超过2次尝试)。 此外,我不知道你,但我不希望PHP通过检查错误的查询结果致命错误失败。 如果你想直接尝试这个代码,你需要在session.php文件的顶部:
var $sess_query_attempts = 5;
还要注意,这不是整个sess_read
函数
$query = $this->CI->db->get($this->sess_table_name); //Multiple AJAX calls checking //But adding add a loop to check a couple more times has stopped premature session breaking $counter = 0; while( !$query && $counter < $this->sess_query_attempts ){ usleep(100000);//wait a tenth of a second $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); $counter++; } if ( !$query || $query->num_rows() == 0) { $this->CI->db->where('session_id', $session['session_id']); $query = $this->CI->db->get( $this->sess_table_name ); $this->sess_destroy(); return FALSE; }
无论如何,这个问题没有一个完整的答案在那里,我觉得我应该分享我的发现,那些可能仍然遇到早期会话超时的网站上使用像我这样的AJAX吨。
在你所有的控制器构造函数中写入session_start()