我正在执行来自会话保护页的标准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将发送一个令牌,但接收另一个令牌,从而使请求失败。所以目前我正在处理设置为false的SESSION_REGENERATE_ID
。有没有办法让getjson在这种情况下工作?
编辑:所有文件都在同一域下。
我们有index.php,其中包含js,querys.php是ajax请求调用的php文件,s_session.php包含上面编写的构造函数。
文件index.html和querys.php在开始时都受到这样的保护:
include "s_session.php";
if(!$login->isLoggedIn()) {
header('Content-Type: application/json');
echo json_encode(array('content' => 'Login failed'));
exit;
}
phpsessid在set cookie下的ajax请求头中。
答案中返回的phpsessid与session_regent_id中预期的不同。
如果session_regent_id设置为false,则请求将顺利通过。如果设置为true,则会收到错误消息“login failed”。
这里是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:以下是完整的评估代码:
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:以下是请求头和响应cookie:
我注意到在
onload
期间执行的第一个getjson是成功的。所有其他在用户之后完成并由用户触发的操作都不成功 最佳答案
这主要是由种族状况引起的,但也可能是浏览器错误。
我可以排除浏览器错误的情况,但提供的信息中存在冲突,更具体地说,在this comment中:
这是几个调用,一个接一个的用户操作,从来没有同时进行。
如果从未同时执行请求,则这只意味着浏览器无法正常工作,并且会发生以下情况之一:
丢弃它在响应中接收到的Set-Cookie
头(如果该逻辑依赖于HttpOnly
标志,这将解释web仍然工作的原因:d)onLoad
事件实际上是在页面加载期间执行的(我知道这没有意义,但如果是浏览器错误,一切都有可能)
当然,这些是不太可能发生的,所以我倾向于说,实际上您一次处理多个ajax请求,在这种情况下,竞争条件是一个合理的场景:
第一个请求开始(初始phpsessid)
第二个请求启动(同样,使用相同的phpsessid)
处理第一个请求并用新的phpsessid接收响应
第二个请求直到现在才被阻止(会话处理程序使用锁定来防止多个进程同时修改同一数据),现在才开始使用初始phpsessid进行处理,此时该phpsessid无效,因此它会触发注销。
我个人会研究由onLoad
事件触发的事件-很容易将所有初始化逻辑都放在其中,而忘记这可能包括多个异步请求。
不管怎样,真正的逻辑错误是这段代码:
if ( SESSION_REGENERATE_ID )
session_regenerate_id(SESSION_REGENERATE_ID);
对于两种不同的情况,使用相同的值:
确定是否重新生成会话ID
告诉
session_regenerate_id()
它是否应该立即销毁与旧会话id关联的数据不立即销毁该数据的选项正是通过异步请求为这些竞争条件提供解决方案,因为它们实际上是不可避免的。一个竞争条件将在某个时刻发生,无论你如何努力避免它-即使没有逻辑缺陷,网络滞后(例如)可能仍然会触发它。
保留旧会话的数据(当然是暂时的)可以解决这个问题,只需允许“延迟”或“不同步”请求处理启动时可用的任何数据。
过期的会话稍后将由会话垃圾收集器清理。这可能并不理想,但对于需要删除数据的存储来说,这几乎是唯一的解决方案(与redis这样的缓存存储不同,redis允许您设置ttl值,而不必手动删除)。
就我个人而言,我更喜欢避免在ajax请求期间重新生成会话id…如你所见,这是一罐虫子。:)