即使我在工作,会话也会过期,会话生命周期,Ajax 和 Symfony2
Session expires even if I'm working, session lifetime, Ajax and Symfony2
如果用户在 10 分钟内什么都不做,我已经将我的应用程序配置为在超时时关闭会话。在 config.yml
我有这个:
session:
handler_id: ~
cookie_lifetime: 600 # 10 minutes
gc_maxlifetime: 600 # 10 minutes
gc_probability: 1
gc_divisor: 1
我每隔一分钟进行一次 Ajax 调用,以检查会话是否即将过期,这就是我检查的内容:
public function isLoggedInAction(Request $request)
{
$response = array();
$response['authenticated'] = FALSE;
$status = 200;
$securityContext = $this->container->get('security.context');
if ($securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
$response['authenticated'] = TRUE;
}
return new JsonResponse($response, $status ?: 200);
}
由于某些 未知 原因不起作用,无论我是否正在使用页面,会话每 10 分钟关闭一次,为什么?我错过了什么?
编辑 1 尝试了新值,但仍然无效:
session:
handler_id: ~
cookie_lifetime: 1800
gc_maxlifetime: 600
gc_probability: 1
gc_divisor: 100
当我在页面上工作、执行 Ajax 调用和一些其他任务时,会话已关闭,因此无法正常工作。到目前为止,显然 对我有用的唯一值是设置 cookie_lifetime: 86400 #1 day
,这对我来说太疯狂了!
编辑 2 在 @acontell 建议修复 VM 时间和日期之后我尝试使用这个新值(需要 10 分钟太长了,所以我改成了 3):
session:
handler_id: ~
cookie_lifetime: 1800
gc_maxlifetime: 180 # session will expire after 3 minutes of inactivity
gc_probability: 1
gc_divisor: 100
我还通过启用 ntpd
服务修复了 VM 上的 date/time,现在日期就可以了:
[root@webvm var]# date
Sun Feb 1 18:35:17 VET 2015
但是 5 分钟后(函数调用执行了 5 次)会话仍然存在。这就是我从 Javascript 端调用函数 isLoggedInAction()
的方式:
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( data.authenticated ){
var timer = window.setInterval(function(){
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( !data.authenticated ){
window.clearInterval(timer);
$.growl({
message: 'La sesión ha expirado por inactividad, debe <a href=""><b>iniciar seción</b></a> nuevamente.'
}, {
type: "danger",
allow_dismiss: false,
timer: 10000,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onHide: function(){
location.reload();
}
});
}
}).fail(function(){});
},60000);
}
}).fail(function(){});
见下图:
测试 3
在说一切正常之后,我做了最新的确定性测试:打开应用程序并在整个晚上(将近 8 小时)保持不变,令人惊讶的是它从未关闭会话。如下图所示,查看页面有多少请求,并查看会话如何仍然存在,为什么?
Ajax 呼叫间隔:10.5 分钟
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( data.authenticated ){
var timer = window.setInterval(function(){
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( !data.authenticated ){
window.clearInterval(timer);
$.growl({
message: 'La sesión ha expirado por inactividad, debe <a href=""><b>iniciar seción</b></a> nuevamente.'
}, {
type: "danger",
allow_dismiss: false,
timer: 10000,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onHide: function(){
location.reload();
}
});
}
}).fail(function(){});
}, 210000);
}
}).fail(function(){});
设置表明会话应该在 10 分钟后过期。
session:
handler_id: ~
cookie_lifetime: 630000
gc_maxlifetime: 630000 # session will expire after 10 minutes of inactivity
gc_probability: 1
gc_divisor: 100
服务器时间正常:
[root@webvm sencamer.dev]# date
Mon Feb 2 07:26:53 VET 2015
我还应该检查什么?
测试 5
好的,我还在做测试,因为这不是一个好的行为。所以,这就是我为这个测试所做的:
- 打开应用程序并开始处理它
- 在某个时刻停止工作并离开应用程序进行 Ajax 调用以检查会话是否仍然存在。 (会话仍然存在,请参见下图)
- 第一次通话后,我继续处理应用程序,如 图像 2 所示,但令人惊讶的会话结束并且应用程序关闭。
为什么?是什么导致了这种行为?根据我的参数是否正确?
此图显示了对函数的第一次也是唯一一次调用
通话结束后我继续工作但会话关闭
尝试使用这些参数:
gc_probability: 0
gc_divisor : 1000
首先,请注意您的 gc_probability
和 gc_divisor
。如果两者都设置为 1,则意味着垃圾收集器 (GC) 进程在每次会话初始化时启动的概率为 gc_probability / gc_divisor = 1/1 = 1
(100%)。
您可以保留默认值或给它一个更大的数字,以减少调用 GC 的机会。
例如:
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
cookie_lifetime: 600 # Ten minutes
gc_probability: 1
gc_divisor: 10000
此外,如果您使用的是虚拟机,请检查您的服务器的日期,生成的会话 cookie 将被标记为 time() + cookie_lifetime
的到期时间,其中时间是从服务器获取的。
如果服务器的日期不正确,cookie 可能会立即过期。想象一下:服务器日期 2015-01-31
,您的浏览器 2015-02-01
。服务器发送在 2015-01-31
晚上 11 点过期的 cookie,您的浏览器收到一个过期日期已过的 cookie。
我知道这个问题很老,但这是一个棘手的问题,它帮助我构建了一些东西,所以这里是你如何在 symfony 4+ 中构建它
在文档中有解释https://symfony.com/doc/current/components/http_foundation/session_configuration.html但仍然很棘手,所以有一个示例配置
parameters.yaml
parameters:
session_lifetime: 1800 # 30 minutes
framework.yaml
framework:
session:
handler_id: 'session.handler.native_file'
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
use_cookies: true
cookie_secure: auto
cookie_samesite: lax
cookie_lifetime: 0 # cookie is destroyed when the browser is close
gc_divisor: 100
gc_probability: 100 # garbage collector process on each request (100/100)
gc_maxlifetime: '%session_lifetime%' # session is destroyed after lifetime of user idle
twig.yaml
twig:
globals:
session_lifetime: '%session_lifetime%' # so you can use the variable in any template
抽象控制器
// extend this one in the controllers
class AbstractController extends SymfonyAbstractController
{
/**
* @Route("/is-logged-in", name="is_logged_in")
*/
public function isLoggedIn(Security $security)
{
$response = ['authenticated' => false];
if ($security->isGranted('IS_AUTHENTICATED_FULLY')) {
$response['authenticated'] = true;
}
return new JsonResponse($response);
}
}
app.js
checkLoggedIn = function (isLoggedInUrl, loginUrl, sessionLifeTime) {
let timer = window.setInterval(function () {
$.ajax({
url: isLoggedInUrl,
method: 'GET',
success: function (data) {
if (data['authenticated'] === true) {
console.log('checkLoggedIn OK');
} else {
window.clearInterval(timer);
// you can use any message system you want here instead of Toasts
$(document).Toasts('create', {
title: 'Warning',
body: 'Session expired, <a href="' + loginUrl + '">please reconnect</a>',
class: 'bg-danger',
});
}
}
})
},
(sessionLifeTime + 60) * 1000 // the login check will be sent 60 sec after session supposed expiration
);
}
your_base_template.html.twig
<script>
checkLoggedIn("{{ path('is_logged_in') }}", "{{ path('security_login') }}", {{ session_lifetime }});
</script>
如果用户在 10 分钟内什么都不做,我已经将我的应用程序配置为在超时时关闭会话。在 config.yml
我有这个:
session:
handler_id: ~
cookie_lifetime: 600 # 10 minutes
gc_maxlifetime: 600 # 10 minutes
gc_probability: 1
gc_divisor: 1
我每隔一分钟进行一次 Ajax 调用,以检查会话是否即将过期,这就是我检查的内容:
public function isLoggedInAction(Request $request)
{
$response = array();
$response['authenticated'] = FALSE;
$status = 200;
$securityContext = $this->container->get('security.context');
if ($securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
$response['authenticated'] = TRUE;
}
return new JsonResponse($response, $status ?: 200);
}
由于某些 未知 原因不起作用,无论我是否正在使用页面,会话每 10 分钟关闭一次,为什么?我错过了什么?
编辑 1 尝试了新值,但仍然无效:
session:
handler_id: ~
cookie_lifetime: 1800
gc_maxlifetime: 600
gc_probability: 1
gc_divisor: 100
当我在页面上工作、执行 Ajax 调用和一些其他任务时,会话已关闭,因此无法正常工作。到目前为止,显然 对我有用的唯一值是设置 cookie_lifetime: 86400 #1 day
,这对我来说太疯狂了!
编辑 2 在 @acontell 建议修复 VM 时间和日期之后我尝试使用这个新值(需要 10 分钟太长了,所以我改成了 3):
session:
handler_id: ~
cookie_lifetime: 1800
gc_maxlifetime: 180 # session will expire after 3 minutes of inactivity
gc_probability: 1
gc_divisor: 100
我还通过启用 ntpd
服务修复了 VM 上的 date/time,现在日期就可以了:
[root@webvm var]# date
Sun Feb 1 18:35:17 VET 2015
但是 5 分钟后(函数调用执行了 5 次)会话仍然存在。这就是我从 Javascript 端调用函数 isLoggedInAction()
的方式:
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( data.authenticated ){
var timer = window.setInterval(function(){
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( !data.authenticated ){
window.clearInterval(timer);
$.growl({
message: 'La sesión ha expirado por inactividad, debe <a href=""><b>iniciar seción</b></a> nuevamente.'
}, {
type: "danger",
allow_dismiss: false,
timer: 10000,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onHide: function(){
location.reload();
}
});
}
}).fail(function(){});
},60000);
}
}).fail(function(){});
见下图:
测试 3
在说一切正常之后,我做了最新的确定性测试:打开应用程序并在整个晚上(将近 8 小时)保持不变,令人惊讶的是它从未关闭会话。如下图所示,查看页面有多少请求,并查看会话如何仍然存在,为什么?
Ajax 呼叫间隔:10.5 分钟
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( data.authenticated ){
var timer = window.setInterval(function(){
$.post(Routing.generate('isLoggedIn',{}),{},'json').done(function (data, textStatus, jqXHR){
if( !data.authenticated ){
window.clearInterval(timer);
$.growl({
message: 'La sesión ha expirado por inactividad, debe <a href=""><b>iniciar seción</b></a> nuevamente.'
}, {
type: "danger",
allow_dismiss: false,
timer: 10000,
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
onHide: function(){
location.reload();
}
});
}
}).fail(function(){});
}, 210000);
}
}).fail(function(){});
设置表明会话应该在 10 分钟后过期。
session:
handler_id: ~
cookie_lifetime: 630000
gc_maxlifetime: 630000 # session will expire after 10 minutes of inactivity
gc_probability: 1
gc_divisor: 100
服务器时间正常:
[root@webvm sencamer.dev]# date
Mon Feb 2 07:26:53 VET 2015
我还应该检查什么?
测试 5
好的,我还在做测试,因为这不是一个好的行为。所以,这就是我为这个测试所做的:
- 打开应用程序并开始处理它
- 在某个时刻停止工作并离开应用程序进行 Ajax 调用以检查会话是否仍然存在。 (会话仍然存在,请参见下图)
- 第一次通话后,我继续处理应用程序,如 图像 2 所示,但令人惊讶的会话结束并且应用程序关闭。
为什么?是什么导致了这种行为?根据我的参数是否正确?
此图显示了对函数的第一次也是唯一一次调用
通话结束后我继续工作但会话关闭
尝试使用这些参数:
gc_probability: 0
gc_divisor : 1000
首先,请注意您的 gc_probability
和 gc_divisor
。如果两者都设置为 1,则意味着垃圾收集器 (GC) 进程在每次会话初始化时启动的概率为 gc_probability / gc_divisor = 1/1 = 1
(100%)。
您可以保留默认值或给它一个更大的数字,以减少调用 GC 的机会。
例如:
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
cookie_lifetime: 600 # Ten minutes
gc_probability: 1
gc_divisor: 10000
此外,如果您使用的是虚拟机,请检查您的服务器的日期,生成的会话 cookie 将被标记为 time() + cookie_lifetime
的到期时间,其中时间是从服务器获取的。
如果服务器的日期不正确,cookie 可能会立即过期。想象一下:服务器日期 2015-01-31
,您的浏览器 2015-02-01
。服务器发送在 2015-01-31
晚上 11 点过期的 cookie,您的浏览器收到一个过期日期已过的 cookie。
我知道这个问题很老,但这是一个棘手的问题,它帮助我构建了一些东西,所以这里是你如何在 symfony 4+ 中构建它
在文档中有解释https://symfony.com/doc/current/components/http_foundation/session_configuration.html但仍然很棘手,所以有一个示例配置
parameters.yaml
parameters:
session_lifetime: 1800 # 30 minutes
framework.yaml
framework:
session:
handler_id: 'session.handler.native_file'
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
use_cookies: true
cookie_secure: auto
cookie_samesite: lax
cookie_lifetime: 0 # cookie is destroyed when the browser is close
gc_divisor: 100
gc_probability: 100 # garbage collector process on each request (100/100)
gc_maxlifetime: '%session_lifetime%' # session is destroyed after lifetime of user idle
twig.yaml
twig:
globals:
session_lifetime: '%session_lifetime%' # so you can use the variable in any template
抽象控制器
// extend this one in the controllers
class AbstractController extends SymfonyAbstractController
{
/**
* @Route("/is-logged-in", name="is_logged_in")
*/
public function isLoggedIn(Security $security)
{
$response = ['authenticated' => false];
if ($security->isGranted('IS_AUTHENTICATED_FULLY')) {
$response['authenticated'] = true;
}
return new JsonResponse($response);
}
}
app.js
checkLoggedIn = function (isLoggedInUrl, loginUrl, sessionLifeTime) {
let timer = window.setInterval(function () {
$.ajax({
url: isLoggedInUrl,
method: 'GET',
success: function (data) {
if (data['authenticated'] === true) {
console.log('checkLoggedIn OK');
} else {
window.clearInterval(timer);
// you can use any message system you want here instead of Toasts
$(document).Toasts('create', {
title: 'Warning',
body: 'Session expired, <a href="' + loginUrl + '">please reconnect</a>',
class: 'bg-danger',
});
}
}
})
},
(sessionLifeTime + 60) * 1000 // the login check will be sent 60 sec after session supposed expiration
);
}
your_base_template.html.twig
<script>
checkLoggedIn("{{ path('is_logged_in') }}", "{{ path('security_login') }}", {{ session_lifetime }});
</script>