Я выполняю стандартный запрос 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
это успешно. Все остальные, сделанные после и запущенные пользователем, не увенчались успехом
Это в основном вызвано состоянием гонки, но возможна ошибка браузера.
я было бы исключить сценарий ошибки браузера, но в предоставленной информации есть конфликт, более конкретно в этот комментарий:
Это несколько звонков, совершаемых один за другим по действиям пользователя, никогда одновременно.
Если запросы никогда не выполняется одновременно, тогда это может означать только то, что ваш браузер не работает должным образом, и происходит одно из следующих действий:
Set-Cookie
заголовок, который он получает в ответе (если эта логика зависит от HttpOnly
флаг, это объяснило бы, почему сеть все еще работает: D)onLoad
событие фактически выполняется во время загрузки страницы (я знаю, что это не имеет смысла, но все возможно, если это ошибка браузера)Конечно, это вряд ли произойдет, поэтому я склонен сказать, что вы на самом деле обрабатываете несколько запросов AJAX одновременно, и в этом случае условие гонки является вероятным сценарием:
Я бы лично посмотрел на то, что вызвано этим onLoad
событие — легко поместить всю логику инициализации туда и забыть, что это может включать в себя несколько асинхронных запросов.
В любом случае, настоящий логический ошибка с вашей стороны это кусок кода:
if ( SESSION_REGENERATE_ID )
session_regenerate_id(SESSION_REGENERATE_ID);
Вы используете одно и то же значение для двух разных условий:
session_regenerate_id()
если он должен немедленно уничтожить данные, связанные со старым идентификатором сеансаОпция не уничтожайте эти данные немедленно, чтобы обеспечить решение этих условий гонки с помощью асинхронных запросов, потому что они практически неизбежны. Состояние гонки будут произойдет в какой-то момент, независимо от того, как сильно вы пытаетесь избежать этого — даже если нет логического недостатка, сетевые лаги (например) могут все же вызвать его.
Сохранение данных старого сеанса (временно, конечно) позволяет обойти эту проблему, просто позволяя «позднему» или «несинхронизированному» запросу работать с любыми данными, которые были доступны на момент его запуска.
Сеансы с истекшим сроком позже будут очищены сессионным сборщиком мусора. Это, возможно, не идеально, но в значительной степени единственное решение для хранилищ, которые требуют удаления данных (в отличие от хранилищ кэша, таких как Redis, которые позволяют вам устанавливать значение TTL вместо необходимости удалять вручную).
Лично я предпочитаю избегать регенерации идентификатора сессии, особенно во время запросов AJAX … как видите, это червь. 🙂
Других решений пока нет …