getJSON и session_regenerate_id ()

Я выполняю стандартный запрос getJSON со страницы, защищенной сессией:

$.getJSON('queries.php',{q: 'updateEvent', param1: p1},
function(data){
...
}
);

В моем конструкторе сеансов я установил следующее:

function startSession()
{
ini_set('session.use_only_cookies', SESSION_USE_ONLY_COOKIES);

$cookieParams = session_get_cookie_params();
session_set_cookie_params(
$cookieParams["lifetime"],
$cookieParams["path"],
$cookieParams["domain"],
SESSION_SECURE,
SESSION_HTTP_ONLY
);

session_start();

if ( SESSION_REGENERATE_ID )
session_regenerate_id(SESSION_REGENERATE_ID);
}

Если я установлю SESSION_REGENERATE_ID в true, тогда мой getJSON отправляет токен, но получает другой, что делает запрос неудачным. На данный момент я имею дело с SESSION_REGENERATE_ID установить в ложь.

Есть ли способ заставить getJSON работать в таких условиях?

РЕДАКТИРОВАТЬ: все файлы находятся в одном домене.

У нас есть файл index.php, в который включен js, у нас есть query.php, который является php-файлом, вызываемым запросами ajax, у нас есть s_session.php, который включает в себя конструктор, написанный выше.

Файлы index.html и query.php оба защищены в начале следующим образом:

include "s_session.php";
if(!$login->isLoggedIn()) {
header('Content-Type: application/json');
echo json_encode(array('content' => 'Login failed'));
exit;
}

PHPSESSID находится в заголовке ajax-запроса в set-cookie.
PHPSESSID, возвращаемый в ответе, отличается от ожидаемого для session_regenerate_id.

Если для SESSION_REGENERATE_ID установлено значение FALSE, запросы выполняются без проблем. Если установлено значение TRUE, то я получаю сообщение об ошибке «Ошибка входа».

Вот isLoggedIn ():

public function isLoggedIn() {
//if $_SESSION['user_id'] is not set return false
if(ASSession::get("user_id") == null)
return false;

//if enabled, check fingerprint
if(LOGIN_FINGERPRINT == true) {
$loginString  = $this->_generateLoginString();
$currentString = ASSession::get("login_fingerprint");
if($currentString != null && $currentString == $loginString)
return true;
else  {
//destroy session, it is probably stolen by someone
$this->logout();
return false;
}
}

$user = new ASUser(ASSession::get("user_id"));
return $user->getInfo() !== null;
}

РЕДАКТИРОВАТЬ 2: Вот полный код ASSession:

class ASSession {

/**
* Start session.
*/
public static function startSession()
{
ini_set('session.use_only_cookies', SESSION_USE_ONLY_COOKIES);

session_start();
$s = $_SESSION;

$cookieParams = session_get_cookie_params();

session_set_cookie_params(
$cookieParams["lifetime"],
$cookieParams["path"],
$cookieParams["domain"],
SESSION_SECURE,
SESSION_HTTP_ONLY
);

if ( SESSION_REGENERATE_ID )
session_regenerate_id(SESSION_REGENERATE_ID);

//$_SESSION = $s;

}

/**
* Destroy session.
*/
public static function destroySession() {

$_SESSION = array();

$params = session_get_cookie_params();

setcookie(  session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);

session_destroy();
}

/**
* Set session data.
* @param mixed $key Key that will be used to store value.
* @param mixed $value Value that will be stored.
*/
public static function set($key, $value) {
$_SESSION[$key] = $value;
}

/**
* Unset session data with provided key.
* @param $key
*/
public static function destroy($key) {
if ( isset($_SESSION[$key]) )
unset($_SESSION[$key]);
}

/**
* Get data from $_SESSION variable.
* @param mixed $key Key used to get data from session.
* @param mixed $default This will be returned if there is no record inside
* session for given key.
* @return mixed Session value for given key.
*/
public static function get($key, $default = null) {
if(isset($_SESSION[$key]))
return $_SESSION[$key];
else
return $default;
}

}

РЕДАКТИРОВАТЬ 3: вот заголовки запроса и куки-файл ответа:

введите описание изображения здесь
введите описание изображения здесь

Я заметил, что самый первый getJSON, который выполняется во время onload это успешно. Все остальные, сделанные после и запущенные пользователем, не увенчались успехом

8

Решение

Это в основном вызвано состоянием гонки, но возможна ошибка браузера.

я было бы исключить сценарий ошибки браузера, но в предоставленной информации есть конфликт, более конкретно в этот комментарий:

Это несколько звонков, совершаемых один за другим по действиям пользователя, никогда одновременно.

Если запросы никогда не выполняется одновременно, тогда это может означать только то, что ваш браузер не работает должным образом, и происходит одно из следующих действий:

  • Отказ от Set-Cookie заголовок, который он получает в ответе (если эта логика зависит от HttpOnly флаг, это объяснило бы, почему сеть все еще работает: D)
  • onLoad событие фактически выполняется во время загрузки страницы (я знаю, что это не имеет смысла, но все возможно, если это ошибка браузера)

Конечно, это вряд ли произойдет, поэтому я склонен сказать, что вы на самом деле обрабатываете несколько запросов AJAX одновременно, и в этом случае условие гонки является вероятным сценарием:

  1. Первый запрос начинается (с вашего начального PHPSESSID)
  2. Второй запрос начинается (опять же с тем же PHPSESSID)
  3. Первый запрос обрабатывается и получает ответ с новым PHPSESSID
  4. Второй запрос был заблокирован до сих пор (обработчик сеанса использует блокировку для предотвращения одновременного изменения одних и тех же данных несколькими процессами) и является прямо сейчас начинает обрабатываться с начальный PHPSESSID, который является недопустимым в этом пункте, следовательно, почему это вызывает выход из системы.

Я бы лично посмотрел на то, что вызвано этим onLoad событие — легко поместить всю логику инициализации туда и забыть, что это может включать в себя несколько асинхронных запросов.


В любом случае, настоящий логический ошибка с вашей стороны это кусок кода:

if ( SESSION_REGENERATE_ID )
session_regenerate_id(SESSION_REGENERATE_ID);

Вы используете одно и то же значение для двух разных условий:

  1. Определение того, нужно ли вообще генерировать идентификатор сеанса
  2. Рассказывая session_regenerate_id() если он должен немедленно уничтожить данные, связанные со старым идентификатором сеанса

Опция не уничтожайте эти данные немедленно, чтобы обеспечить решение этих условий гонки с помощью асинхронных запросов, потому что они практически неизбежны. Состояние гонки будут произойдет в какой-то момент, независимо от того, как сильно вы пытаетесь избежать этого — даже если нет логического недостатка, сетевые лаги (например) могут все же вызвать его.
Сохранение данных старого сеанса (временно, конечно) позволяет обойти эту проблему, просто позволяя «позднему» или «несинхронизированному» запросу работать с любыми данными, которые были доступны на момент его запуска.

Сеансы с истекшим сроком позже будут очищены сессионным сборщиком мусора. Это, возможно, не идеально, но в значительной степени единственное решение для хранилищ, которые требуют удаления данных (в отличие от хранилищ кэша, таких как Redis, которые позволяют вам устанавливать значение TTL вместо необходимости удалять вручную).

Лично я предпочитаю избегать регенерации идентификатора сессии, особенно во время запросов AJAX … как видите, это червь. 🙂

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]