Ajax 丢失会话

Ajax is losing sessions

我将我的 Symfony 应用程序从 Symfony 4.0.7 升级到 Symfony 4.1,之后 AJAX 调用丢失了会话值。

我同时调用了大约 6 个 ajax 请求。他们中的第一个进展顺利,但其他人正在失去会话价值。它仅在迁移到 Symfony 4.1 之后发生,并且仅用于 AJAX 调用。有什么想法吗?

edit:只有在同时调用 ajax 时才会发生。当我在调用 ajax 之间添加例如 100 毫秒的延迟时,一切正常。

edit2:它发生在 4 个不同的服务器上。 2 台开发服务器、1 台测试服务器和 1 台实时服务器。全部 运行 在 NGINX 和 php7

可能的原因如下::

Allow to cache requests that use sessions:

每当 session 在请求期间启动时,Symfony 都会将响应转换为私有 non-cacheable 响应以防止泄露私有信息。但是,在某些情况下,甚至可以缓存使用 session 的请求。

例如,可以为属于该组的所有用户缓存与某个用户组相关的信息。处理这些高级缓存场景超出了 Symfony 的范围,但它们可以通过 FOSHttpCacheBundle 解决。

为了禁用使用 session 不可缓存的请求的默认 Symfony 行为,在 Symfony 4.1 中,我们添加了 NO_AUTO_CACHE_CONTROL_HEADER header,您可以将其添加到响应中:

use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
$response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 'true');

Deprecate some uses of Request::getSession()

当不存在 session 时使用 Request::getSession() 在 Symfony 4.1 中已被弃用,它会在 Symfony 5.0 中抛出异常。解决方案是始终首先使用 Request::hasSession() 方法检查 session 是否存在:

if ($request->hasSession() && ($session = $request->getSession())) {
    $session->set('some_key', 'some_value');
}

有关参考的更多信息:Here

你检查过XMLHttpRequest.withCredentials了吗?

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials

响应头怎么样?

  1. 访问控制允许凭据:真
  2. 连接:保持活动状态
  3. 设置 cookie:...

这些请求的会话 ID 是否相等?

http://php.net/manual/en/function.session-id.php

也许您需要在 ajax 请求之前设置会话 cookie。

举个例子:

ajax.php

<?php
function getSessionIdentifier()
{
    if (!session_id()) {
        session_start();
    }
    if (!isset($_SESSION['identifier'])) {
        $_SESSION['identifier'] = bin2hex(random_bytes(5));
    }
    return $_SESSION['identifier'];
}

echo json_encode([
    'identifier' => getSessionIdentifier()
]);

开始-没有-session.php

<!DOCTYPE html>
<html>
<head><title>start without session</title></head>
<body>
<script>
let started;
const cookie = () => '[' + document.cookie + ']';
const track = () => {
    started = started || (new Date()).getTime();
    return ((new Date()).getTime() - started) + 'ms';
};

const send = (index) => {
    const req = new XMLHttpRequest();
    req.open('GET', '/ajax.php');

    console.log(track(), 'send', index, cookie());

    req.addEventListener("load", () => {
        console.log(track(), 'receive', index, cookie(), req.responseText);
    });

    req.send();
}

document.cookie = "PHPSESSID=;expires=Thu, 01 Jan 1970 00:00:00 GMT";

console.log(track(), 'begin', cookie());
const len1 = 5;
const len2 = 10;
Array.from({length: len1}).forEach((v, i) => send(i));
console.log(track(), 'delay');
Array.from({length: len2}).forEach((v, j) => window.setTimeout(() => send(j + len1), j * 10));
</script>
</body></html>

开始-session.php

<?php
session_start();
$_SESSION['identifier'] = bin2hex(random_bytes(5));
?><!DOCTYPE html>
<html>
<head><title>start with session</title></head>
<body>
<script>
let started;
const cookie = () => '[' + document.cookie + ']';
const track = () => {
    started = started || (new Date()).getTime();
    return ((new Date()).getTime() - started) + 'ms';
};

const send = (index) => {
    const req = new XMLHttpRequest();
    req.open('GET', '/ajax.php');

    console.log(track(), 'send', index, cookie());

    req.addEventListener("load", () => {
        console.log(track(), 'receive', index, cookie(), req.responseText);
    });

    req.send();
}

//
// document.cookie = "PHPSESSID=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
//

console.log(track(), 'begin', cookie());
const len1 = 5;
const len2 = 10;
Array.from({length: len1}).forEach((v, i) => send(i));
console.log(track(), 'delay');
Array.from({length: len2}).forEach((v, j) => window.setTimeout(() => send(j + len1), j * 10));
</script>
</body></html>

我不是 100% 确定这是相同的情况。需要更多信息。

当您的应用程序依赖于实时 cookie 时,您不得同时启动所有 AJAX 请求。 也许 symfony 使用 cookie 中的某些东西来完成 CSRF 令牌工作。

它在请求之间使用 100 毫秒超时的原因是第一个请求有时间解析响应并使用该响应更改 cookie,而下一个请求使用新的 cookie。

您需要做的是:

  • 使第二个 AJAX 从第一个调用回调,第三个从第二个调用回调,依此类推...

  • 找出 cookie 中需要更新的内容才能不产生错误并禁用它。但请注意,这种保护是有原因的,如果禁用,您的应用程序可能会面临安全漏洞。 如果您提供错误的 AJAX 调用响应(浏览器中的 inpect->network)
  • ,它可能会帮助我们解决问题

  • 让你的 ajax 调用使用基于 JWT 的中间件( 如果你以前从未这样做过会很复杂)所以它使用无状态会话。

好的,所以问题是因为会话固定策略会更改每个请求的会话 ID,并且 AJAX 个请求确实有时间更新新 ID。

解决方法很简单,设置session_fixation_strategy: none即可。