答案是系统时钟偏移超5分钟导致jwt签名验证失败。需先用date和curl -i https://packagist.org | grep date比对时间偏差,≥60秒即需校准;linux应启用systemd-timesyncd并停用chronyd,wsl2还需sudo hwclock --hctosys同步硬件时钟。

系统时钟偏移超过 5 分钟,会导致 Composer 的 JWT 签名验证失败和元数据缓存失效,composer install 或 composer update 会直接报 Signature verification failed 或 filemtime(): stat failed,此时清缓存、换镜像、重装 Composer 全部无效——必须先校准时间。
如何快速确认是系统时钟问题
别猜,直接比对:
- 运行
date,看输出时间是否明显异常(比如年份是 2022 或 2026 年) - 执行
curl -I https://packagist.org 2>/dev/null | grep Date,提取响应头中的Date:字段 - 对比
date -R输出的本地时间与上一步的Date值,偏差 ≥60 秒就已危险;≥5 分钟即触发 JWT 拒绝,≥15 分钟则 HTTPS 证书校验也失败 - 在 WSL2 中还要额外检查:
sudo hwclock --hctosys后再跑date,因为 WSL2 虚拟机时钟可能休眠漂移数小时
Linux 服务器上启用 systemd-timesyncd 校准
手动 date -s 是临时补丁,还会破坏时区逻辑。现代 Linux 应用 systemd-timesyncd 持续守护:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 检查状态:
timedatectl status,重点看System clock synchronized: no和NTP service: inactive - 启用服务:
sudo timedatectl set-ntp true - 若
chronyd正在运行(ps aux | grep chronyd),先停用:sudo systemctl stop chronyd && sudo systemctl disable chronyd,避免两个 NTP 服务争抢时钟 - 验证效果:
timedatectl timesync-status,poll interval应为 32s–1024s,root dispersion应
容器与 CI 环境中时间同步的特殊处理
容器默认共享宿主机 clock,但 CI 节点或 WSL2 可能启动时未完成 NTP 同步就执行 composer install:
- Docker 容器需挂载宿主机时区:
-v /etc/localtime:/etc/localtime:ro,否则date显示错误 - GitHub Actions/GitLab CI 中,不要依赖环境默认时间,加
--no-cache并显式指定镜像源:composer install --repository=https://mirrors.aliyun.com/composer/ --no-cache - WSL2 永久修复:在
/etc/wsl.conf中添加[boot] systemd=true,并确保 Windows 的「Windows Time」服务已启用,之后执行wsl --shutdown重启 - 云 CI 首次运行失败、重试成功?大概率是 NTP 同步延迟导致,可在作业开头加等待逻辑(如
while ! timedatectl show --property=NTPSynchronized --value | grep -q 'yes'; do sleep 1; done)
为什么镜像配置和 clear-cache 都救不了时间问题
镜像源只代理数据,不修改 Packagist 返回的 Date 响应头和 JWT 中的 iat/exp 时间戳;composer clear-cache 只删本地文件,不影响 PHP 扩展(如 firebase/php-jwt)对令牌的实时校验逻辑:
- 哪怕你用的是阿里云镜像,
curl -I https://mirrors.aliyun.com/composer/packages.json返回的Date头仍与本地时间比对 -
composer config --global repo.packagist composer https://mirrors.aliyun.com/composer/配置再正确,只要本地时间不准,签名层就会静默拒绝整个响应体 - 报错里从不提“时间”,只显示
filemtime(): stat failed(解压缓存时触发)或Invalid JWT signature(JWT 解析时触发) - 真正要修的不是缓存路径或镜像 URL,而是
/dev/rtc或CLOCK_REALTIME这个底层时钟源
时间不准是所有上层配置的底层依赖,它会让镜像、缓存、代理、甚至 composer.lock 的版本解析全部失效——修之前,任何 composer update 都只是在重复失败。










