Sesión de Codeigniter interviniendo con llamadas ajax

Mi aplicación CodeIgniter utiliza la biblioteca de sesiones y guarda los datos en la base de datos.

He estado teniendo problemas cuando se crean sesiones en blanco después de una determinada llamada ajax.

Al investigar, parece que hubo 2 llamadas a funciones simultáneas que se activaron y requieren una validación de la sesión. Uno fallaría y el otro estaría bien.

Pude arreglar esto al no hacer que se dispararan simultáneamente. Pero todavía no entiendo la razón por la cual falla. ¿Tiene que ver con una llamada actualizando la cookie del usuario y la segunda llamada invalidando? ¿O tal vez cuando lee el DB muere de alguna manera?

Revisé un poco la clase principal de la sesión y no encontré ninguna pista sobre la causa.

Si alguien tiene el mismo problema antes de agradecer cualquier consejo sobre cómo depurar o cuál es la causa.

¡Gracias!

EDITAR:

Originalmente dije que había un retorno de estado 408. Ese fue un caso no relacionado.

Esta es la función que dispara MyVar.refresh () en paralelo:

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('' + re.error); MyVar.refresh(); } },'json'); MyVar.refresh(); return stopDefault(event); }; 

SOLUCIONES POSIBLES:

Encontrado esto: http://codeigniter.com/forums/viewthread/102456/

Aparentemente no funciona bien con ajax. Una solución es no permitir la actualización de la sesión si se trata de una llamada ajax; El único problema es que nuestro sitio está construido principalmente con ajax ..

Además, acaba de bajar el sess_time_to_update a algo muy frecuente y ajax estaba haciendo bien. También hizo una actualización del navegador y no se agotó. No estoy seguro de por qué si la identificación de la sesión ya ha cambiado con una llamada ajax y nunca se actualizaron las cookies del navegador.

Prueba esto

 input->is_ajax_request()) { parent::sess_update(); } } } // ------------------------------------------------------------------------ /* End of file MY_Session.php */ /* Location: ./application/libraries/MY_Session.php */ 

El problema está en la función sess_update de la clase de sesión, que genera un nuevo session_id después de X segundos. Cada página tiene un session_id, si el session_id expira antes de que se realice la llamada ajax, esa llamada fallará.

Cree un archivo php en / application / libraries / con el nombre MY_Session (o el prefijo que haya configurado), pegue este código allí y eso es todo. Esta función anulará la función sess_update en la clase de sesión, verificando cada solicitud si esa solicitud fue hecha por ajax, omitiendo la función sess_update.

Es una mala idea establecer la sess_expiration en valores más altos. Esta es una característica de seguridad que lo protegerá contra el secuestro de la sesión

PD: no soy muy fluido en inglés, si no entiendes algo solo házmelo saber.

Hasta que se fusione en la twig estable, la solución (¡finalmente!) Es usar la confirmación 245bef5 de Areson combinada con el esquema de la base de datos:

 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`) ); 

Para obtener más información, lea extraer 1283 comentarios de arriba a abajo.

Tuvimos este problema, fue debido al parámetro sess_time_to_update en config.php. CI lo usa para actualizar la ID de la sesión a una nueva. Si el cambio ocurre en una llamada ajax, CI envía una nueva cookie para decirle al navegador la nueva ID de la sesión. Desafortunadamente, los navegadores parecen ignorar esta cookie y mantener la ID de sesión anterior.

Lo solucionamos estableciendo sess_time_to_update en sess_expiration en la configuración.

 $config['sess_time_to_update'] = $config['sess_expiration']; 

También tuve este problema en codeigniter versión 2.1.3, cuando uso la siguiente configuración:

 $config['sess_use_database'] = TRUE; $config['sess_time_to_update'] = 300; 

Creo que no tiene nada que ver con las solicitudes ajax, sino con un error en codeigniter.

Parece que cuando almacena la sesión en la base de datos, se fuerza un cierre de sesión después de 300 segundos. Después de 3 horas de búsqueda y análisis, encontré un error claro en el código y uno incierto también, he resuelto el error de la siguiente manera:

Crea un nuevo archivo: MY_Session.php en la carpeta de aplicaciones / bibliotecas

Agregue el siguiente código:

 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 ); } } ?> 

El error claro es que no almacenó el ‘user_data’ en la cookie actualizada. El error poco claro es que ejecuta la función sess_read () en el archivo Session.php después de actualizar la nueva identificación de la sesión, no sé por qué sucede esto, porque esperaba que se ejecute antes de actualizar y no después como está escrito en el constructor de Session.php. Entonces, la función sess_read () comienza a leer la información de cookies anterior con la identificación de sesión anterior y desea compararla con la id de sesión en la base de datos, pero después de la actualización de session_id ya no está en la base de datos, por lo que se cierra la sesión.

Esta línea de código en la función sess_read del archivo Session.php es responsable de leer la información de cookies anterior:

 $session = $this->CI->input->cookie($this->sess_cookie_name); 

Así que en la función _set_cookie de MY_Session.php agregué esta línea de código para actualizar la vieja información de cookies del servidor con la nueva:

 $_COOKIE[ $this->sess_cookie_name ] = $cookie_data; // added to solve bug 

Con esta corrección, el ‘sess_time_to_update’ en combinación con ‘sess_use_database’ debería funcionar bien. Esta es una solución de error simple y simple.

Tuve exactamente el mismo problema al cargar imágenes con ajax, y configuré sess_expiration en config para:

 $config['sess_expiration'] = time()+10000000; 

Y solucionó mi problema.

Bueno, las buenas soluciones están aquí. hacer cualquier cosa con sess_time_to_update etc probar las soluciones a continuación

  1. https://degreesofzero.com/article/fixing-the-expiring-session-problem-in-codeigniter.html
  2. http://ellislab.com/forums/viewthread/138823/#725078

al número de solución “1” Actualizo un pequeño script más. después de haber trabajado mucho con CI, entiendo que hay dos razones para perder Sesiones CI. una es cuando se hacen malas llamadas ajax, la sesión se ACTUALIZA y las sesiones se pierden; el segundo es después de que la llamada mala ajax efectúe la función sess_destroy en la biblioteca SESSION de CI. POR ESO HICE UN PEQUEÑO CAMBIO PARA “1” SOLUCIÓN QUE ES

 /*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(); } } 

Espero que te ayude a la gente también

Parece haber un error en la sesión central de manejo de clase de sesiones de CI.

Encontré una biblioteca de sesión alternativa que funciona como un encanto.

Biblioteca de sesión alternativa de CI

Recomendaría ampliar la clase principal CI_Session en lugar de reemplazarla.

Para ampliar, crea un archivo MY_Session.php en la application/libraries . Pegue el contenido de la biblioteca alternativa, reemplace la class CI_Session por la class MY_Session extends CI_Session .

Elimine las _flashdata_mark() protegidas de _flashdata_mark() , _flashdata_sweep() , _get_time() , _set_cookie() , _serialize() , _unserialize() , _sess_gc() .

Espero eso ayude.

Parece que todavía hay muchas versiones anteriores de CI en uso y quería agregar mis dos centavos, a pesar de que este hilo es antiguo. Acabo de pasar unos días resolviendo el problema de las llamadas AJAX en Code Igniter y tengo una solución que cubre los principales problemas, aunque parte de la solución no es “maravillosa”. La versión de CI que estoy (todavía) usando es 2.1.3

Mi aplicación requiere que las llamadas AJAX actualicen el campo last_activity para mantener una sesión válida, por lo que no es suficiente para que simplemente abandone la actualización de la sesión en las llamadas AJAX.

La comprobación de errores para sess_update y sess_read no es adecuada en esta versión de CI (no he investigado versiones más recientes) y muchos de los problemas comienzan allí.

Primera parte: sess_update()

Múltiples llamadas AJAX crean condiciones de carrera que resultan en un locking de la base de datos para las llamadas posteriores. Si intentamos ejecutar una consulta de actualización pero la base de datos está bloqueada, obtenemos un error, la consulta devuelve falso, pero ¿la cookie se actualiza con datos nuevos? … ¡MALO! Además, no necesitamos un nuevo session_id para cada llamada Ajax. Solo necesitamos actualizar last_activity. Prueba esto:

  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); } } 

parte 2: sess_read()

Problema muy similar aquí … La base de datos a veces se bloquea durante una consulta. Excepto que no podemos ignorar los errores esta vez. Estamos tratando de leer la sesión para ver si existe … así que si obtenemos un error de base de datos bloqueada, podemos verificar el error y volver a intentarlo (un par de veces si fuera necesario). En mis pruebas, nunca hice más de 2 bashs en). Además, no sé ustedes, pero no quiero que php falle en un error fatal al no verificar un resultado falso de la consulta. Lo necesitará en la parte superior del archivo session.php si quiere probar este código directamente:

var $sess_query_attempts = 5;

También tenga en cuenta que esta no es toda la función 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; } 

De todos modos, no hay una respuesta completa a este problema y sentí que debería compartir mis hallazgos con aquellos que todavía pueden experimentar tiempos de espera tempranos en sitios que usan toneladas de AJAX como la mía.

escribe session_start() en todo el constructor de tu controlador