无尘阁日记

无尘阁日记

双方服务器时间戳不一致,导致凭证大概率过期
2026-05-08

我问:

现在遇到一个问题,我这里颁发的一个凭证,时间戳可能比对方超前,然后加上60,到他那里就出现了有效期不正确的提示,我该怎么弄?把过期时间改小?

AI答:

不要优先把过期时间“改小”。真正问题不是 60 秒太长,而是 双方服务器时间不一致

你现在的问题本质是:

你的服务器时间:12:00:30
对方服务器时间:12:00:00

你生成 expireTime = 12:01:30
对方拿到后判断:这个凭证有效期还有 90 秒,超过允许的 60 秒
于是提示:有效期不正确

也就是说,对方不是觉得它“过期了”,而是觉得它“有效期太长”或“签发时间在未来”。


一、正确处理顺序

1. 第一优先级:校准服务器时间

这是根治方案。

在你的服务器上先看时间:

date
timedatectl status

开启 NTP 自动校时:

sudo timedatectl set-ntp true

如果是 CentOS / Rocky / AlmaLinux,可以看:

chronyc tracking
systemctl status chronyd

如果你的程序跑在 Docker 里,注意:
容器时间一般跟宿主机走,所以要校准宿主机时间,不是只看容器。


二、如果短期无法让双方时间完全一致,怎么兼容?

你可以做一个“时间回拨补偿”。

比如你发现你们服务器普遍比对方快 30 秒,那就不要用本机当前时间直接签发,而是:

签发基准时间 = 当前时间 - 30 秒
过期时间 = 签发基准时间 + 60 秒

也就是:

$ttl = 60;
$clockOffset = 30; // 你方时间比对方快约 30 秒,就回拨 30 秒

$baseTime = time() - $clockOffset;
$expireTime = $baseTime + $ttl;

$payload = [
    'sub' => json_encode([
        'thirdparty' => 'norming',
        'module' => 'SELFBUILT',
        'tenantId' => $tenantId,
        'creditCode' => $creditCode,
        'expireTime' => $expireTime,
        'nonce' => bin2hex(random_bytes(8)),
    ], JSON_UNESCAPED_UNICODE),
    'iat' => $baseTime,
    'exp' => $expireTime,
];

这样对方看到的是:

iat:更接近对方当前时间
expireTime:距离对方当前时间约 60 秒

比单纯把过期时间改小更稳。


三、为什么不建议直接把过期时间改小?

比如你直接从:

expireTime = 当前时间 + 60

改成:

expireTime = 当前时间 + 30

短期可能能过,但会带来两个问题:

第一,真实可用时间变短。网络慢一点、接口排队一下、重试一次,凭证可能就过期了。

第二,时间偏差一变又会失败。今天你们快 30 秒,明天快 45 秒,后天 NTP 恢复正常,它又不稳定了。

所以更好的做法是:

不是单纯缩短有效期,而是修正签发基准时间。

四、最好让对方加“时间容忍窗口”

如果能跟对方沟通,建议他们校验时允许一定时间误差,比如 ±60 秒或 ±120 秒。

正确校验逻辑应该类似:

iat 不能明显晚于对方当前时间,比如不能超过 now + 60
expireTime 不能早于对方当前时间,比如不能小于 now - 60
expireTime - iat 应等于或不超过 60 秒

不要死板地判断:

expireTime - 当前时间 必须小于等于 60 秒

因为这个判断天然怕服务器时间误差。


五、你们这个“诺明凭证”建议统一成这样

你之前那个 token 里有两个过期时间:

sub.expireTime:60 秒后
JWT 外层 exp:7 天后

这个不太好,容易导致对方系统校验逻辑分裂。

建议统一:

iat = 签发基准时间
exp = 签发基准时间 + 60
sub.expireTime = 签发基准时间 + 60

也就是外层 exp 和内层 expireTime 保持一致。


六、最终建议

你现在不要简单理解成“把过期时间改小”。

更准确的处理是:

1. 先校准你方服务器时间;
2. 如果短期仍有偏差,就做签发时间回拨补偿;
3. iat、exp、sub.expireTime 使用同一个基准时间;
4. 最好让对方加 60~120 秒 clock skew 容忍;
5. 如果偏差超过 60 秒,靠凭证参数硬调不可靠,必须校时。

你可以临时先这样改:

baseTime = time() - 30
expireTime = baseTime + 60

如果对方仍提示有效期不正确,再把 30 调成 4560。但这只能算临时兜底,最终还是要把服务器时间同步好。