假设有一个站点/系统登录了会员区,用户在使用站点/系统时很少,但非常不方便地注销。
它可疑地过期了,因为用户没有闲置很长时间。即使它们处于空闲状态,我也添加了一个定期的 AJAX 请求,即所谓的检测信号,它更新会话的访问时间和修改时间。我什至在每次用户单击某些内容或调用心跳时添加 touch($session_file)。我也尝试重新生成会话ID。没有任何帮助。
不幸的是,到目前为止,我无法在本地重现该问题,因为当有更多请求时,它经常发生。一些 php.ini 参数:
session.use_cookies = 1
session.use_only_cookies = 1
session.cookie_lifetime = 0
session.gc_probability = 1
session.gc_divisor = 1500
session.gc_maxlifetime = 10800
由于会话和身份验证已经通过代码中的一个超级控制器处理,因此至少应该很容易排除会话销毁。
通常只有登录页面会创建会话,因此此时您可以(并且应该)在内部添加已知值,例如会话 ID。
其他页面(包括您的检测信号)恢复现有会话,因此此时您查找上述值;如果缺少该值,您可以再执行一些检查:
- 会话 cookie 是否通过?如果没有,则为浏览器/cookie 问题。
- 会话 cookie 是否与
session_id()
对应? 如果不是,会话文件由于垃圾回收而丢失。
会话 - 中是否存在已知值?如果不存在,则会话被截断或有人试图进行会话采用攻击。
- 已知值是否与会话 Cookie 相对应? 如果不是,则会话是通过与 cookie 不同的方式建立的;您可以检查
session.use_only_cookies
设置。
上述检查应该为您指明正确的方向。
我想你使用的是内置的PHP文件会话存储?它存在已知的竞争条件问题。
当有来自同一会话 ID 的并发请求时,我遇到了丢失会话 ID 的类似问题。由于文件被第一个请求锁定,因此所有其他并发连接都无法访问文件,其中一些连接生成了新的会话 ID。这些情况也非常罕见,我花了很长时间才找到问题所在。从那以后,我使用 memcached 进行会话存储,这些问题消失了。
如果没有进一步的信息,我很难回答这个问题,但是您确定这只会在您有更多流量时才发生。可以做什么:
- 添加代码
- 操作系统发行版和版本
- PHP版本
您是否有任何依赖于 IP 的会话检查。如果是这样,则可能是您实际上正在使用一个代理,该代理每隔一段时间(例如Cloudflare)提供一次不同的IP,在这种情况下,您可以使用HTTP_CF_CONNECTING_IP
来获取实际恒定的IP。
我还注意到一件事:
session.use_only_cookies = 1
session.cookie_lifetime = 0
因此,当浏览器关闭时,cookie实际上将被销毁。是否有可能因为用户关闭选项卡(在某些浏览器中确实会清除 cookie)而导致连接丢失?
正如其他人所建议的那样,我会强烈地说您移动到数据库会话。使用中央会话存储可以帮助停止竞争条件,它不会完全阻止它们,但它确实会使其更加困难。此外,如果您真的获得了那么多会话 ID,您可能需要使用内置 PHP 会话 ID 以外的其他内容进行研究,但是内置哈希非常独特,重复的可能性非常低,所以我认为这不是您的问题。
澄清编辑:
如果您使用的是与Cloudflare不同的代理,那么您当然需要获取代理集的HTTP标头,使用Cloudflare就是一个例子。
用过的东西。它使用javascript以10分钟的间隔"ping"服务器。
<form method="POST" target="keepalive-target" id="keepalive-form">
<input type="hidden" name="keepalive-count" id="keepalive-count">
</form>
<iframe style="display:none; width:0px; height:0px;" name="keepalive-target"></iframe>
<script>
var kaCount=0;
setInterval(function()
{
document.getElementById("keepalive-count").value=kaCount++
document.getElementById("keepalive-form").submit();
},600000);
</script>
所有提供的答案都显示了对问题的良好洞察力,但我只需要分享我的确切问题的解决方案。我终于能够重现问题并修复它。
因此,系统包含两个子系统,例如管理员和客户端界面。管理员在另一个选项卡中以客户端身份登录时意外注销,并在以管理员身份登录时注销客户端界面。它这样做是因为所有内容都写入了一个带有命名空间的会话。我所做的是删除在注销操作时不断销毁会话的代码,并将其替换为会话命名空间取消设置并替换为仅有权访问登录页面的命名空间的来宾会话。