【问题标题】:PHP Session discrepancy between development (localhost) and hosted service开发(本地主机)和托管服务之间的 PHP 会话差异
【发布时间】:2021-12-04 13:57:14
【问题描述】:

我正在实现一个 google 登录功能,该功能使用跨页面的 PHP 会话来确定用户是否实际上已登录。这在我的本地计算机上完美运行。 当我上传到我的托管服务器(可能是谷歌云)并相应地调整客户端 ID 然后正常登录时,登录过程完成,但用户被报告为未登录。一些页面刷新和/ 或单击“登录”按钮然后导致用户被识别为已登录。我假设会话变量设置不正确或设置它们有一些延迟。但是,也许还有另一个我不知道的问题(当我在本地运行服务时再次没有问题)。 我很欣赏这是一个有点模糊的问题。 我尝试使用 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)

完成后,服务器会加载login.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');
    }
}

还有什么我可以看的建议吗?该解决方案在我的本地机器上运行良好,只是在我上传和托管时效果不佳。

下面的cmets,

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 开始有效。难道仅仅是因为谷歌的身份验证服务器和托管的网络服务器(顺便说一下,谷歌云)之间存在时间同步问题吗? 我还在 error_log 中注意到 $_SESSION['auth'] 是“未定义的索引”,这表明身份验证根本没有发生 - 尽管经过几次刷新后,身份验证才会起作用,这可能支持时间同步问题。不过不太确定我能做些什么!

进一步更新。 看起来问题是由于时间差异造成的。其实在google API库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;
    }

但这仍然不起作用。我还是被难住了!

【问题讨论】:

  • 从登录到未登录错误的具体路径是什么?例如,它是否从登录重定向到个人资料编辑页面?如果出现错误并随后登录状态发生变化,这是新用户还是现有用户?
  • 请通过session_id(); 验证您的页面请求之间的会话ID 相同。如果会话 ID 在您的页面请求之间发生变化,您将获得新的(空)会话。
  • 谢谢两位。我在上面的问题中添加了更多内容。
  • 您尝试过什么来解决问题?你被困在哪里了? “可能仅仅是因为 Google 的身份验证服务器和托管的网络服务器之间存在时间同步问题” - 是的,这听起来很有效
  • 请不要在供应商文件夹中编辑代码。下次您使用 Composer 安装或更新软件包时,此类更改将被删除

标签: php session-variables google-signin


【解决方案1】:

Crykey!这需要一些时间才能弄清楚。 $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(按照建议),并以某种方式确定该值未被读取。然后我开始直接编辑供应商代码。完成这段旅程后,我已经证明了我的 include/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 登录,而不是 firebase)

   /**
     * 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);
    }

【讨论】:

  • 酷!我不知道所有的代码。所以我想知道为什么他在一个地方不知道 $leeway。但如果修复有效,那就没问题了。方式就是目标;-)
猜你喜欢
  • 2021-10-29
  • 2016-01-05
  • 2022-01-08
  • 2012-10-24
  • 2018-04-03
  • 2011-02-11
  • 2019-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多