PHP 开发(本地主机)和托管服务之间的会话差异

PHP Session discrepancy between development (localhost) and hosted service

我正在实施 google 登录功能,该功能使用跨页面的 PHP 会话来确定用户是否确实已登录。这在我的本地计算机上非常有效。 当我上传到我的托管服务器(恰好是 Google 云)并相应地调整客户端 ID,然后正常登录时,登录过程完成,但用户被报告为未登录。A几次页面刷新和/或单击“登录”按钮然后导致用户被识别为已登录。我假设会话变量设置不正确或者设置它们有一些延迟。但是,也许还有另一个我不知道的问题(当我在本地 运行 服务时同样没有问题)。 我明白这是一个有点模糊的问题。 我已经尝试使用 session_write_close() 以防会话保持打开的时间过长,尽管这没有明显的区别。

一旦用户通过 google 登录成功验证,POST 页面,oauth.php 读取变量并将它们写入 $_SESSION 变量,例如:

session_start();
...
$_SESSION["auth"] = true;
$_SESSION["userId"] = $row['id']; // A SQL query and further logic either populates this field (existing user) or leaves it blank (new user)

完成后,服务器会加载 loggedin.php,确定这是新用户还是现有用户,然后分别加载新用户表单或主页:

session_start();
if (!$_SESSION['auth']) {
    print("     You must be signed in to edit your profile.");
} else {
    if ($_SESSION['userId']) { // If userId is set, existing user
        header('Location: index.php');
    } else { // userId is not set, new user
        header('Location: profileedit.php');
    }
}

关于我还可以查看的内容有什么建议吗?该解决方案在我的本地机器上完美运行,只是在我上传和托管它时运行不佳。

根据下面的评论,

session_id() 在我的本地机器上从 index.php 到 login.php 到 oauth.php 到 profile.php 是一致的。但是,当我浏览托管服务器上的 error_log 时,我看到以下错误消息:

[Sun Oct 17 17:24:32.094053 2021] [php7:error] [pid 25817] [client XXX:51629] PHP Fatal error:  Uncaught Firebase\JWT\BeforeValidException: Cannot handle token 
prior to 2021-10-17T17:24:54+0000 in /var/www/html/vendor/firebase/php-jwt/src/JWT.php:142\nStack trace:\n#0 /var/www/html/vendor/google/apiclient/src/AccessToken/Verify.php
(106): Firebase\JWT\JWT::decode()\n#1 /var/www/html/vendor/google/apiclient/src/Client.php(793): Google\AccessToken\Verify->verifyIdToken()\n#2 /var/www/html/includes/oa
uth.php(18): Google\Client->verifyIdToken()\n#3 {main}\n  thrown in /var/www/html/vendor/firebase/php-jwt/src/JWT.php on line 142, referer: http://XXX/login.
php

错误记录在 17:24:32 并且令牌似乎设置为从 17:24:54 开始有效。可能只是 Google 的身份验证服务器和托管 Web 服务器(顺便说一句,还有 Google 云)之间存在时间同步问题? 我还在 error_log 中注意到 $_SESSION['auth'] 是“未定义的索引”,这表明身份验证根本没有发生 - 尽管经过几次刷新后,身份验证确实有效,这可能支持时间同步问题。不过不太确定我能做些什么!

进一步更新。 看起来问题确实是由于时间差异造成的。其实在googleAPI库vendor/firebase/php-jwt/src/JWT.php中有如下注释和代码:

    /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 1;

我试图在我的代码中使用以下方法覆盖它:

$jwt = new \Firebase\JWT\JWT; //Allow for discrepancies between server and auth times
$jwt::$leeway = 5;

但这没有用。于是我直接编辑了vendor/firebase/php-jwt/src/JWT.php中的代码:

    /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 5;

我也编辑了 vendor/google/apiclient/src/AccessToken/Verify.php 如下:

//      if (property_exists($jwtClass, 'leeway') && $jwtClass::$leeway < 1) { // Original code. Remove the $leeway<1 constraint as the $leeway would not be < 1
    if (property_exists($jwtClass, 'leeway')) {
      // Ensures JWT leeway is at least 1
      // @see https://github.com/google/google-api-php-client/issues/827
      $jwtClass::$leeway = 5;
    }

但这仍然不起作用。我还是很困惑!

天哪!这花了一些时间才弄明白。 $leeway 的单位是秒。将此值增加 5(按照建议)对我没有影响,因为我的网络服务器上的时钟比身份验证服务器慢 24 秒。我能够通过将以下错误记录添加到 vendor/firebase/php-jwt/src/JWT.php:

来推断这一点
        // Check the nbf if it is defined. This is the time that the
        // token can actually be used. If it's not yet that time, abort.
        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to (nbf) ' . \date(DateTime::ISO8601, $payload->nbf) . ' Current time ' . \date(DateTime::ISO8601, $timestamp) . ' leeway ' . \date(DateTime::ISO8601, $tim
estamp + static::$leeway)
            );
        }
        // Check that this token has been created before 'now'. This prevents
        // using tokens that have been created for later use (and haven't
        // correctly used the nbf claim).
        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
            throw new BeforeValidException(
                'Cannot handle token prior to (iat) ' . \date(DateTime::ISO8601, $payload->iat) . ' Current time ' . \date(DateTime::ISO8601, $timestamp) . ' leeway ' . \date(DateTime::ISO8601, $tim
estamp + static::$leeway)
            );
        }
        // Check if this token has expired.
        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
            throw new ExpiredException('Expired token');
        }

这导致以下 error.log 输出:

[Sun Oct 17 23:01:59.120593 2021] [php7:notice] [pid 25837] [client XXX:53053] oauth.php JWT server time mismatch. Retry attempt: 9 Error: Firebase\JWT\BeforeValidException: Cannot handl
e token prior to (iat) 2021-10-17T23:02:23+0000 Current time 2021-10-17T23:01:59+0000 leeway 2021-10-17T23:02:04+0000 in /var/www/html/vendor/firebase/php-jwt/src/JWT.php:142\nStack trace:\n#0 /var/
www/html/vendor/google/apiclient/src/AccessToken/Verify.php(106): Firebase\JWT\JWT::decode()\n#1 /var/www/html/vendor/google/apiclient/src/Client.php(793): Google\AccessToken\Verify->verifyIdTok
en()\n#2 /var/www/html/includes/oauth.php(24): Google\Client->verifyIdToken()\n#3 {main}, referer: http://XXX/login.php

最初我将 $leeway 的值设置为 5(按照建议)并以某种方式确定未读取该值。然后我开始直接编辑供应商代码。完成此旅程后,我已经证明 includes/oauth.php 脚本中的以下代码确实具有增加 $leeway 属性.

的预期效果
$jwt = new \Firebase\JWT\JWT; //Allow for discrepancies between server and auth times
$jwt::$leeway = 100;

如果您正在寻找答案,那就是上面的答案。为了完整起见,我在旅途中发现了以下内容...

在 vendor/firebase/php-jwt/src/JWT.php 中设置 $leeway 的工具存在,但实际上并没有在我的实现中使用(可能是因为我正在使用 google 登录,而不是火力基地)

   /**
     * When checking nbf, iat or expiration times,
     * we want to provide some extra leeway time to
     * account for clock skew.
     */
    public static $leeway = 100;

因此,我在 vendor/google/apiclient/src/AccessToken/Verify.php 中设置了余地。此外,令人沮丧的是,原始代码完全忽略了设置值,因为 属性 不存在,所以我添加了一个 else 子句来设置 属性 无论如何:

      if (property_exists($jwtClass, 'leeway') && $jwtClass::$leeway < 1) {
      // Ensures JWT leeway is at least 1
      // @see https://github.com/google/google-api-php-client/issues/827
      $jwtClass::$leeway = 100;
      error_log("Property exists. Updating leeway", 0);
    } else {
      $jwtClass::$leeway = 100;
      error_log("Property does not exist. Updating leeway", 0);
    }