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
响应头怎么样?
- 访问控制允许凭据:真
- 连接:保持活动状态
- 设置 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
即可。
我将我的 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
响应头怎么样?
- 访问控制允许凭据:真
- 连接:保持活动状态
- 设置 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
即可。