Sessão do Codeigniter com ligações ajax

Meu aplicativo CodeIgniter usa a biblioteca de session e salva os dados no database.

Eu tenho tido alguns problemas onde as sessões em branco são criadas após uma determinada chamada de ajax.

Ao investigar, parece que houve duas chamadas de funções simultâneas triggersdas que exigem uma validação de session. Um falharia e o outro estaria bem.

Consegui consertar isso ao não tê-los triggersdos simultaneamente. Mas eu ainda não entendo a razão pela qual isso falha. Tem a ver com uma chamada atualizando o cookie do usuário e a segunda chamada invalidando? Ou talvez quando ler o DB ele morre de alguma forma?

Eu examinei um pouco a class principal da Sessão e não encontrei nenhuma pista para a causa.

Se alguém teve o mesmo problema antes eu gostaria de receber conselhos sobre como depurar ou qual é a causa.

Obrigado!

EDITAR:

Eu originalmente disse que havia um retorno de status 408. Esse foi um caso não relacionado.

Esta é a function que triggers MyVar.refresh () em 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); }; 

SOLUÇÕES POSSÍVEIS:

Encontrou este: http://codeigniter.com/forums/viewthread/102456/

Aparentemente, não joga bem com ajax. Uma solução é proibir a atualização de session se for uma chamada de ajax; O único problema é que o nosso site é principalmente construído com ajax ..

Além disso, apenas baixei o sess_time_to_update para algo muito frequente e o ajax estava indo bem. Também fiz uma atualização do navegador e não tempo limite. Não sei por que, se a ID da session já foi alterada após uma chamada ajax e os cookies do navegador nunca foram atualizados.

Tente isso

 < ?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 */ 

O problema está na function sess_update da class de session, que gera um novo session_id após X segundos. Cada página tem um session_id, se o session_id expirar antes que a chamada do ajax seja feita, essa chamada falhará.

Crie um arquivo php em / application / libraries / com o nome MY_Session (ou qualquer prefixo que você tenha definido), cole este código lá e é só isso. Esta function irá sobrescrever a function sess_update na class de session, checando cada requisição se o pedido foi feito por ajax, pulando a function sess_update.

É uma má idéia definir o sess_expiration em valores mais altos. Este é um recurso de segurança que irá protegê-lo contra a session hijaking

PD: Eu não sou muito fluente em inglês, se você não entender alguma coisa, me avise.

Até que ele seja mesclado na ramificação estável, a solução (finalmente!) É usar o commit 245bef5 da Areson combinado com o esquema do database:

 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 mais informações, leia pull 1283 comments top-to-bottom.

Nós tivemos esse problema, foi devido ao parâmetro sess_time_to_update em config.php. O CI usa isso para atualizar o ID da session para um novo. Se a mudança ocorrer em uma chamada ajax, o IC envia um novo cookie para informar ao navegador o novo ID da session. Infelizmente, os navegadores parecem ignorar esse cookie e manter o antigo ID da session.

Nós o corrigimos definindo o sess_time_to_update para sess_expiration na configuração.

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

Eu tive esse problema também no codeigniter versão 2.1.3, quando eu uso a seguinte configuração:

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

Eu acho que não tem nada a ver com pedidos de ajax, mas sim com um bug no codeigniter.

Parece que quando você armazena a session no database, um logout é forçado após 300 segundos. Após 3 horas de pesquisa e análise, encontrei um erro claro no código e um pouco claro também, eu resolvi o erro da seguinte forma:

Crie um novo arquivo: MY_Session.php na pasta application / libraries

Adicione o seguinte código a ele:

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

O erro óbvio é que ele não armazenou o ‘user_data’ no cookie atualizado. O erro não está claro é que ele executa a function sess_read () no arquivo Session.php depois de atualizar o novo ID de session, eu não sei porque isso acontece, porque eu esperava que ele seja executado antes de atualizar e não depois como está escrito no construtor de Session.php. Portanto, a function sess_read () começa a ler as informações do cookie antigo com o id da session antiga e deseja compará-lo com o id da session no database, mas após a atualização session_id ele não está mais lá no database, portanto, causa o logout.

Esta linha de código na function sess_read do arquivo Session.php é responsável por ler as informações antigas do cookie:

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

Então na function _set_cookie do MY_Session.php eu adicionei esta linha de código para atualizar as informações antigas do cookie do servidor com a nova:

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

Com essa correção, o ‘sess_time_to_update’ em combinação com ‘sess_use_database’ deve funcionar bem. Esta é uma correção de bug simples e simples.

Eu tive exatamente o mesmo problema ao carregar imagens com o ajax, e configurei o sess_expiration em config para:

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

E isso resolveu o meu problema.

Bem, as boas soluções estão aqui. faça qualquer coisa com sess_time_to_update etc tente as soluções abaixo

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

para o número da solução “1.” Eu atualizo um pequeno script mais. depois de crackear muito com CI eu recebo os pontos que existem duas razões para perder de CI SESSIONS. um é quando chamadas ruins de ajax são feitas, a session é UPDATED e as sessões são perdidas; o segundo é depois do mau ajax chamar isso afeta a function sess_destroy na biblioteca SESSION do CI. Então eu fiz uma pequena mudança para “1”. SOLUÇÃO QUE É

 /*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 isso ajude as pessoas também

Parece haver uma falha na session principal de manipulação da class de session do CI.

Encontrou uma biblioteca de session alternativa que funciona como um encanto.

Biblioteca de session alternativa de CI

Eu recomendaria estender a class principal CI_Session em vez de substituí-la.

Para estender, crie um arquivo MY_Session.php em application/libraries . Cole o conteúdo da biblioteca alternativa, substitua a class CI_Session pela class MY_Session extends CI_Session .

Remover protegido das _flashdata_mark() , _flashdata_sweep() , _get_time() , _set_cookie() , _serialize() , _unserialize() , _sess_gc() .

Espero que ajude.

Parece que ainda há muitas versões de CI mais antigas em uso e eu queria adicionar meus dois centavos, mesmo que esse segmento seja antigo. Eu passei apenas alguns dias resolvendo o problema das chamadas AJAX no Code Igniter e tenho uma solução que cobre os principais problemas, embora parte da solução não seja “maravilhosa”. A versão do CI que eu ainda estou usando é a 2.1.3

Meu aplicativo requer que as chamadas AJAX atualizem o campo last_activity para manter uma session válida, portanto, não é bom o suficiente para eu simplesmente abandonar a atualização da session em chamadas AJAX.

A verificação de erros para sess_update e sess_read é inadequada nesta versão do CI (não investiguei versões mais recentes) e muitos dos problemas começam aí.

Parte um: sess_update()

Várias chamadas AJAX criam condições de corrida que resultam em um database bloqueado para as chamadas posteriores. Se tentarmos executar uma consulta de atualização, mas o database estiver bloqueado, obtemos um erro, a consulta retorna false, mas o cookie ainda é atualizado com novos dados? … RUIM! Além disso, não precisamos de um novo session_id para cada chamada do Ajax. Precisamos apenas atualizar last_activity. Tente isto:

  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 muito similar aqui … Às vezes, o database é bloqueado durante uma consulta. Exceto que não podemos ignorar os erros desta vez. Estamos tentando ler a session para ver se ela existe … então, se obtivermos um erro de database bloqueado, poderemos verificar o erro e tentar novamente (algumas vezes, se necessário). Nos meus testes eu nunca fiz mais do que 2 tentativas). Além disso, eu não sei sobre você, mas eu não quero que o php falhe em um erro fatal por não verificar um resultado de consulta falso. Você precisará disso no topo do arquivo session.php se quiser experimentar este código diretamente:

var $sess_query_attempts = 5;

Observe também que esta não é a function sess_read inteira

 $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 qualquer forma, não há uma resposta completa para esse problema e eu senti que deveria compartilhar minhas descobertas com aqueles que ainda podem estar passando por tempos limite de sessões em sites que usam toneladas de AJAX como o meu.

escreva session_start() em todo o seu construtor de controlador