jQuery Codeigniter 会话因 ajax 调用而出错
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7980193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Codeigniter session bugging out with ajax calls
提问by lamp_scaler
My CodeIgniter app uses the session library and saves data to the DB.
我的 CodeIgniter 应用程序使用会话库并将数据保存到数据库。
I've been having some problems where blank sessions are created after a certain ajax call.
我遇到了一些问题,在某个 ajax 调用之后创建了空白会话。
Upon investigating, it seems that there were 2 simultaneous functions calls that fired off that require a session validation. One would fail and the other would be fine.
经过调查,似乎有 2 个同时触发的函数调用需要会话验证。一个会失败,另一个会很好。
I was able to fix this by not having them fire off simultaneously. But I still don't understand the REASON why it fails. Does it have to do with one call updating the user cookie and the 2nd call invalidating? Or maybe when reading the DB it dies somehow?
我能够通过不让它们同时开火来解决这个问题。但我仍然不明白它失败的原因。这是否与更新用户 cookie 的一次调用和第二次调用无效有关?或者也许在读取数据库时它以某种方式死了?
I looked over the Session core class a bit and have not found any clues to the cause.
我查看了 Session 核心类,并没有找到任何原因的线索。
If any one had the same problem before I would appreciate any advice on how to debug or what the cause is.
如果有人在我之前遇到过同样的问题,我将不胜感激有关如何调试或原因是什么的任何建议。
Thanks!
谢谢!
EDIT:
编辑:
I originally said there was a 408 status return. That was an unrelated case.
我最初说有一个 408 状态返回。那是一个无关的案例。
This is the function that fires off MyVar.refresh() in parallel:
这是并行触发 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);
};
POSSIBLE SOLUTIONS:
可能的解决方案:
Found this: http://codeigniter.com/forums/viewthread/102456/
发现这个:http: //codeigniter.com/forums/viewthread/102456/
Apparently it doesn't play well with ajax. One solution is to disallow session update if it is an ajax call; only problem is that our site is mostly built with ajax..
显然它不能很好地与 ajax 配合使用。一种解决方案是,如果是 ajax 调用,则禁止会话更新;唯一的问题是我们的网站主要是用 ajax 构建的。
Also, just lowered the sess_time_to_update to something very frequent and ajax was doing fine. Also did a browser refresh and it did not timeout. Not sure why if the session ID has already changed upon an ajax call and browser cookies were never updated.
另外,只是将 sess_time_to_update 降低到非常频繁的程度并且 ajax 做得很好。还刷新了浏览器,但没有超时。不知道为什么会话 ID 在 ajax 调用时已经更改并且浏览器 cookie 从未更新。
回答by Agustin Baez
Try this
尝试这个
<?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 */
The problem is in the sess_update function of the session class, that generates a new session_id after X seconds. Every page have a session_id, if the session_id expires before the ajax call is made, that call will fail.
问题出在会话类的 sess_update 函数中,它在 X 秒后生成一个新的 session_id。每个页面都有一个 session_id,如果 session_id 在 ajax 调用之前到期,则该调用将失败。
Create a php file in /application/libraries/ with the name MY_Session (or whatever prefix you set), paste this code there and that is all. This function will override the sess_update function in the session class, checking on every request if that request was made by ajax, skipping the sess_update function.
在 /application/libraries/ 中创建一个名为 MY_Session(或您设置的任何前缀)的 php 文件,将此代码粘贴到那里即可。此函数将覆盖会话类中的 sess_update 函数,检查每个请求是否由 ajax 发出,跳过 sess_update 函数。
Its a bad idea set the sess_expiration at higher values. This is a security feature that will protect you against session hijaking
将 sess_expiration 设置为更高的值是一个坏主意。这是一项安全功能,可保护您免受会话劫持
PD: i'm not very fluent in english, if you dont understand something just let me know.
PD:我的英语不是很流利,如果你不明白什么就让我知道。
回答by Jordan Arseno
Until it is merged into the stable branch, the solution (finally!) is to use Areson's commit 245bef5combined with the database schema:
在它合并到稳定分支之前,解决方案(终于!)是使用 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`)
);
For more information, read pull 1283 commentstop-to-bottom.
有关更多信息,请从上到下阅读pull 1283 条评论。
回答by Francois Gelinas
We had this problem, it was due to the sess_time_to_update parameter in config.php. CI use this to update the session ID to a new one. If the change happen in an ajax call, CI sends a new cookie to tell the browser the new session ID. Unfortunatly, browsers seems to ignore this cookie and keep the old session ID.
我们遇到了这个问题,这是由于 config.php 中的 sess_time_to_update 参数造成的。CI 使用它来将会话 ID 更新为新的。如果更改发生在 ajax 调用中,CI 会发送一个新的 cookie 来告诉浏览器新的会话 ID。不幸的是,浏览器似乎忽略了这个 cookie 并保留了旧的会话 ID。
We fixed it by setting the sess_time_to_update to sess_expiration in the config.
我们通过在配置中将 sess_time_to_update 设置为 sess_expiration 来修复它。
$config['sess_time_to_update'] = $config['sess_expiration'];
回答by user1881928
I had this problem too in codeigniter version 2.1.3, when i use the following configuration:
当我使用以下配置时,我在 codeigniter 版本 2.1.3 中也遇到了这个问题:
$config['sess_use_database'] = TRUE;
$config['sess_time_to_update'] = 300;
I think it has nothing to do with ajax requests but rather with a bug in codeigniter.
我认为这与 ajax 请求无关,而是与 codeigniter 中的错误有关。
It seems that when you store the session in the database a logout is forced after 300 seconds. After 3 hours of searching and analyzing i found a clear bug in the code and a unclear one as well, i've solved the bug as follows:
似乎当您将会话存储在数据库中时,会在 300 秒后强制注销。经过3个小时的搜索和分析,我发现代码中有一个明显的错误,还有一个不清楚的错误,我已经解决了这个错误,如下所示:
Create a new file: MY_Session.php in the application/libraries folder
在 application/libraries 文件夹中创建一个新文件:MY_Session.php
Add the following code to it:
向其中添加以下代码:
<?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
);
}
}
?>
The clear bug is that it didn't store the 'user_data' in the updated cookie. The unclear bug is that it executes the function sess_read() in file Session.php after updating the new session id, i don't know why this happens, because i expected that it executes before updating and not after like it's written in the constructor of Session.php. So the sess_read() function starts reading the old cookie information with the old session id and wants to compare it with session id in the database, but after the session_id update it's not there anymore in the database, so this causes the logout.
明显的错误是它没有在更新的 cookie 中存储“user_data”。不清楚的错误是它在更新新会话 id 后执行文件 Session.php 中的函数 sess_read(),我不知道为什么会发生这种情况,因为我希望它在更新之前执行而不是像在构造函数中编写的那样执行Session.php。所以 sess_read() 函数开始用旧的 session id 读取旧的 cookie 信息,并希望将其与数据库中的 session id 进行比较,但是在 session_id 更新后它不再存在于数据库中,因此这会导致注销。
This line of code in function sess_read of the Session.php file is responsible for reading the old cookie information:
Session.php文件的sess_read函数中的这行代码负责读取旧的cookie信息:
$session = $this->CI->input->cookie($this->sess_cookie_name);
So in function _set_cookie of MY_Session.php i added this line of code to update the old cookie information of the server with the new one:
所以在 MY_Session.php 的 _set_cookie 函数中,我添加了这行代码来用新的更新服务器的旧 cookie 信息:
$_COOKIE[ $this->sess_cookie_name ] = $cookie_data; // added to solve bug
With this fix the 'sess_time_to_update' in combination with 'sess_use_database' should work fine. This is a plain and simple bug fix.
有了这个修复,'sess_time_to_update' 与 'sess_use_database' 结合应该可以正常工作。这是一个简单明了的错误修复。
回答by khalrd
well the good solutions are here. do anything with sess_time_to_update etc try the below solutions
那么好的解决方案就在这里。用 sess_time_to_update 等做任何事情尝试以下解决方案
- https://degreesofzero.com/article/fixing-the-expiring-session-problem-in-codeigniter.html
- http://ellislab.com/forums/viewthread/138823/#725078
- https://degreesofzero.com/article/fixing-the-expiring-session-problem-in-codeigniter.html
- http://ellislab.com/forums/viewthread/138823/#725078
to solution number "1." i update a little script more. after cracking a lot with CI i get the points that there are two reasons for losting of CI SESSIONS. one is when bad ajax calls are made the session get UPDATED and sessions get lost; second is after bad ajax call it effects the sess_destroy function in SESSION library of CI. SO I MADE A LITTLE CHANGE FOR "1." SOLUTION WHICH IS
到解决方案编号“1”。我更新了一个小脚本。在用 CI 破解了很多之后,我得到了 CI SESSIONS 丢失有两个原因的观点。一种是当发生错误的 ajax 调用时,会话被更新并且会话丢失;其次是在错误的ajax调用之后它会影响CI的SESSION库中的sess_destroy函数。所以我对“1”做了一点改动。解决方案
/*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();
}
}
hope ths helps u people also
希望这也能帮助你们
回答by georgesamper
I had the exact same problem when uploading images with ajax, and i set the sess_expiration
in config to:
使用ajax上传图像时遇到了完全相同的问题,我将sess_expiration
配置设置为:
$config['sess_expiration'] = time()+10000000;
And it fixed my problem.
它解决了我的问题。
回答by Zeshan
There seems to be a flaw in the core CI session class handling session.
核心 CI 会话类处理会话中似乎存在缺陷。
Found an alternate session library which works like a charm.
找到了一个替代会话库,它的作用就像一个魅力。
I would recommend to extend the core CI_Session class rather than replacing it.
我建议扩展核心 CI_Session 类而不是替换它。
To extend, create a file MY_Session.php
in application/libraries
. Paste the content of the alternate library, replace class CI_Session
to class MY_Session extends CI_Session
.
要扩展,请MY_Session.php
在application/libraries
. 粘贴备用库的内容,替换class CI_Session
为class MY_Session extends CI_Session
.
Remove protectedfrom _flashdata_mark()
, _flashdata_sweep()
, _get_time()
, _set_cookie()
, _serialize()
, _unserialize()
, _sess_gc()
functions.
从, , , , , ,函数中删除protected。_flashdata_mark()
_flashdata_sweep()
_get_time()
_set_cookie()
_serialize()
_unserialize()
_sess_gc()
Hope it helps.
希望能帮助到你。
回答by Squivo
There seems to still be a lot of older CI versions in use and I wanted to add my two cents, even though this thread is old. I just spent a few days solving the problem of AJAX calls in Code Igniter and I have a solution that covers the main issues, although some of the solution isn't 'wonderful'. The CI version that I am ( still ) using is 2.1.3
似乎仍然有很多旧的 CI 版本在使用,我想添加我的两分钱,即使这个线程很旧。我只花了几天时间解决 Code Igniter 中的 AJAX 调用问题,我有一个涵盖主要问题的解决方案,尽管其中一些解决方案并不“出色”。我(仍然)使用的 CI 版本是2.1.3
My application requires that AJAX calls update the last_activity field to maintain a valid session, so it is not good enough for me to simply abandon updating the session on AJAX calls.
我的应用程序要求 AJAX 调用更新 last_activity 字段以维持有效的会话,所以我简单地放弃在 AJAX 调用上更新会话是不够的。
The error checking for sess_update and sess_read are inadequate in this CI version ( I have not investigated more recent versions ) and a lot of the problems start there.
在这个 CI 版本中,sess_update 和 sess_read 的错误检查是不够的(我没有调查更新的版本),很多问题都是从那里开始的。
Part one: sess_update()
第一部分: sess_update()
Multiple AJAX calls create race conditions which result in a locked the database for the later calls. If we try to run an update query but the database is locked, we get an error, the query returns false, but the cookie is still updated with new data?... BAD! Also, we don't need a new session_id for every Ajax call. We only need to update last_activity. Try this:
多个 AJAX 调用会产生竞争条件,从而导致数据库被锁定以供后续调用使用。如果我们尝试运行更新查询但数据库被锁定,我们会得到一个错误,查询返回 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);
}
}
part 2: sess_read()
第2部分: sess_read()
Very similar problem here... The database is sometimes locked during a query. Except we can't ignore the errors this time. We are trying to read the session to see if it exists... so if we get a locked database error, we can check for the error and try again( a couple times if need be ). In my testing I never made it more than 2 tries in ). Also, I don't know about you, but I don't want php to fail on a fatal error by not checking for a false query result. You'll need this at the top of the session.php file if you want to try this code directly:
非常相似的问题在这里...数据库有时在查询过程中被锁定。只是这次我们不能忽略这些错误。我们正在尝试读取会话以查看它是否存在...因此,如果我们遇到锁定的数据库错误,我们可以检查错误并重试(如果需要,可以尝试几次)。在我的测试中,我从未尝试过超过 2 次)。另外,我不了解您,但我不希望 php 因不检查错误的查询结果而因致命错误而失败。如果你想直接尝试这段代码,你需要在 session.php 文件的顶部使用它:
var $sess_query_attempts = 5;
var $sess_query_attempts = 5;
Also note, this isn't the entire sess_read
function
另请注意,这不是全部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;
}
Anyway, imho there isn't a complete answer to this problem out there and I felt like I should share my findings with those who may still be experiencing early session timeouts on sites which use tons of AJAX like mine.
无论如何,恕我直言,这个问题没有一个完整的答案,我觉得我应该与那些在像我这样使用大量 AJAX 的网站上仍然遇到早期会话超时的人分享我的发现。
回答by Durgam Thirupathi
write session_start()
in all your controller constructor
写入session_start()
所有控制器构造函数