页面之间的 PHP 会话丢失 - 行为因服务器而异
Posted
技术标签:
【中文标题】页面之间的 PHP 会话丢失 - 行为因服务器而异【英文标题】:PHP sessions lost between pages - behaves differently depending on server 【发布时间】:2016-03-17 07:50:04 【问题描述】:我花了几个月的时间在我的域上开发一个应用程序。这总体上是一个简单的概念。在开发过程中,我将其托管在自己的域中,但最近将其推送到我们的实际域中。问题是没有在页面之间创建或保留会话,我一生都无法弄清楚原因。
为下面的代码墙道歉,但我更喜欢它而不是理论解释。
让我们从我如何在每一页的顶部开始我的会话:
function sec_session_start()
$session_name = 'login';
$secure = false;
$httponly = true;
ini_set('session.use_only_cookies', 1);
session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly);
session_name($session_name);
session_start();
session_regenerate_id();
然后我如何检查用户是否已登录。我添加了return x;
而不是false
进行调试。我将此附加到重定向 URL。
function login_check($mysqli)
if(isset($_SESSION['id'], $_SESSION['login_string'], $_SESSION['type']))
$id = $_SESSION['id'];
$login_string = $_SESSION['login_string'];
$user_browser = $_SERVER['HTTP_USER_AGENT'];
if($_SESSION['type'] == 1 || $_SESSION['type'] == 2) // Admin user
if ($stmt = $mysqli->prepare("SELECT `password` FROM `users` WHERE `id` = ? LIMIT 1"))
$stmt->bind_param('s', $id);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows == 1)
$stmt->bind_result($password);
$stmt->fetch();
$login_check = hash('sha512', $password.$user_browser);
if($login_check == $login_string)
return true;
else
return 1;
else
return 2;
else
return 3;
else if($_SESSION['type'] == 3) // Standard user
if($stmt=$mysqli->prepare("SELECT `password` FROM `proj` WHERE `id` = ? LIMIT 1"))
$stmt->bind_param("s", $_SESSION['id']);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows == 1)
$stmt->bind_result($db_key);
$stmt->fetch();
$login_check = hash('sha512', $db_key.$user_browser);
if($login_check == $login_string)
return true;
else
return 4;
else
return 5;
else
return 6;
我有两个登录页面,一个用于管理员,一个用于用户。
管理员:
<?php
ini_set('display_errors','On');
error_reporting(E_ALL);
include_once "../functions.php";
include_once "../db_connect.php";
sec_session_start();
if(login_check($mysqli) === true)
header('Location: ../index.php');
else
// Login form
用户:
<?php
ini_set('display_errors','On');
error_reporting(E_ALL);
include_once "../functions.php";
include_once "../db_connect.php";
sec_session_start();
if(login_check($mysqli) === true)
header('Location: ../index.php');
else
// Login form
完全一样,除了文件来源,因为管理员login.php
位于/admin
。尽管如此,第一个正确显示登录表单,而第二个将您重定向到index.php
(即使在隐身状态下,对我来说也是个谜),导致重定向循环(因为index.php
将其发回以让 nog 登录) .
除此之外,当我使用正确的凭据登录时,它确实将我定向到 index.php
,只是将我重定向回 login.php
,错误代码为 6
。
如果您想了解更多信息或代码示例,请发表评论,我现在迷失在自己的项目中。
任何帮助表示赞赏, 谢谢
12 月 17 日更新:
经过几个小时的调试,我们得出结论,问题不在于代码,而在于服务器配置。一个简单的例子:
<?php
session_start();
echo session_id();
?>
如果您在生产服务器上打开此文件并刷新页面,它会在每个请求中显示一个新的会话 ID。我目前不知道为什么。我已经确认创建了会话文件以及 cookie。它包含正确的信息,并且可以通过具有服务器权限的 SSH 访问。确实有些奇怪的行为。
有什么线索吗?
【问题讨论】:
确保每个请求只启动一次会话,例如,如果多个文件在同一个请求中调用sec_session_start()
,会发生什么?
不同的服务器配置会以不同的方式处理会话,例如通过 cookie 或文件等。所以会话的初始化方式会有所不同,这些只是需要考虑的一般 cmets
1) 您是否在不同的服务器上设置了本地php.ini
? session.savepath
等设置具有帐户特定地址,这些地址将在您的测试和生产服务器上更改。 2) 你的两台服务器是否安装了相同的 PHP 版本? 3)运行phpinfo
,比较两台服务器上的输出设置,比较差异(如会话相关地址)。这些有没有给你任何好奇的答案?
您存储会话的服务器地址是否已正确设置和启用?如具有正确的权限,并且该文件夹存在等?这些都是目前我正在运行的简单试错调试
感谢您的 cmets。 1)我在我的问题中没有提到的是它托管在一个子域上,如果有任何兴趣的话。在主域上托管了一个 WordPress,它运行良好。这会以某种方式影响会话路径吗? 2) 是的,或者非常相似。 3)我会试试这个并寻找差异!关于会话路径,我首先使用session_get_cookie_params()
并将其用作保存路径和域。现在的情况只是我第一次尝试解决问题的结果。
【参考方案1】:
我也遇到了这个问题,但它是 ssl 安装,它用 https 更改了基本文件链接,并且发送文件没有 ssl 链接。我也想到了会话长度。除此之外,我作为您的托管服务提供商可以解释这一点。
【讨论】:
【参考方案2】:我过去有过类似的经历,我不记得我是如何解决这个问题的,但据我所知,它与会话cookie和可变顺序有关> 在 PHP 中。
在 php.ini 中寻找下面这一行,
variables_order = "GPCS"
不知何故,更改这些变量的顺序会影响 PHP 会话。
如果通过任何更改,像我一样,您无法更改服务器上的设置,您也可以尝试使用常规 cookie 复制会话 cookie:
session_start();
set_cookie(session_name(), session_id());
另外注意,不同的浏览器对会话 cookie 也有不同的行为,例如:safari。在 safari 上,无论变量顺序如何,我都必须复制 cookie。
希望对你有帮助
【讨论】:
【参考方案3】:几个月前,我在生产服务器上遇到了类似的问题,其中会话即将过期(例如在您的情况下,会话在页面之间丢失)并且用户突然从我的网站注销。因此,经过几天的调试,一切都归结为 php.ini 文件中的这三行。
session.gc_divisor session.gc_maxlifetime session.gc_probability来自the manual,
session.gc_probability 与 session.gc_divisor 一起用于管理 gc(垃圾收集)例程启动的概率。默认为 1。
session.gc_divisor 加上 session.gc_probability 定义了每次会话初始化时 gc(垃圾收集)进程启动的概率。概率是通过使用 gc_probability/gc_divisor 计算的,例如1/100 表示 GC 进程有 1% 的机会在每个请求上启动。 session.gc_divisor 默认为 100。
session.gc_maxlifetime 指定数据将被视为“垃圾”并可能被清除的秒数。垃圾收集可能在会话开始期间发生(取决于 session.gc_probability 和 session.gc_divisor)。
这些是我在 php.ini 文件中的原始会话配置,实际上首先造成了整个麻烦,
session.gc_divisor 1000 1000
session.gc_maxlifetime 1440 1440
session.gc_probability 0 0
所以这意味着垃圾收集器将以 session.gc_probability 除以 session.gc_divisor 的概率开始。并使用该选项的默认值(分别为 0 和 1000),垃圾收集概率 = session.gc_probability/session.gc_divisor 即 0/1000 = 0%,我认为这是不正确的,加上 session.gc_maxlifetime 对我来说似乎太低了.
所以,我决定在我的 php.ini 文件中更改会话配置并包含这些值,
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 28800
从那以后它工作正常。 所以我给你的建议是,检查你的 php.ini 文件中的这三个值,如果需要,根据你的需要更改它们。
【讨论】:
不幸的是,这并没有改变任何东西。我用今天的发现更新了我的问题。 请原谅..!!【参考方案4】:我浏览了你的代码,除了一件事之外,我没有发现任何不寻常的地方。
您永远不应该使用==
进行字符串比较===
可以。
$something = 0;
echo ('password123' == $something) ? 'true' : 'false';
运行上面的代码,你会发现会话丢失的原因。在您的函数 login_check 中,您使用 ==
比较两个字符串
if($login_check == $login_string)
替换为:
if($login_check === $login_string)
其他一切都很好。如果更改这个小东西不能解决您的问题,请告诉我。
建议
您在会话开始之前连接到数据库。我建议您导入您的函数,然后开始您的会话,然后连接到您的数据库。
include_once "../functions.php";
sec_session_start();
include_once "../db_connect.php";
【讨论】:
明天我会尽快尝试这个。如果这是解决方案,而且很可能是这样,为什么它在两个域之间的行为不同? 你能贴出关于如何设置会话的代码吗?由于这里的事情是正确的,那么你必须错误地设置会话 你具体指的是什么代码?我将会话设置在我发布的第一个括号中,然后在后面的括号中开始。 能否在 session_set_cookie_params() 之前先尝试使用 session_name() 您可以尝试在 session_set_cookie_params() 之前先使用 session_name() 吗?它有时会导致错误。请检查它【参考方案5】:检查会话是否已经开始并确保它只完成一次:
使用 PHP >= 5.4:
function sec_session_start()
if (session_status() == PHP_SESSION_NONE)
$session_name = 'login';
$secure = false;
$httponly = true;
ini_set('session.use_only_cookies', 1);
session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly);
session_name($session_name);
session_start();
session_regenerate_id();
PHP 5.4 之前的版本:
function sec_session_start()
if (session_id() == '')
$session_name = 'login';
$secure = false;
$httponly = true;
ini_set('session.use_only_cookies', 1);
session_set_cookie_params(86400, '/', '.domain.com', $secure, $httponly);
session_name($session_name);
session_start();
session_regenerate_id();
另外,为什么每次都要重新生成会话?
【讨论】:
我会试试这个,但两台服务器都是最新的,在这种情况下应该没有区别。关于再生:它目前对每个请求都执行,因为它是保持其安全的简单方法。话虽如此,它可以减少到不是每个新的请求。我不认为请求的数量是这里的问题,而且在我之前使用过代码的之前的案例中也没有。以上是关于页面之间的 PHP 会话丢失 - 行为因服务器而异的主要内容,如果未能解决你的问题,请参考以下文章