otp-php 并非官方包,正确名称为 spomky-labs/otphp,需用 composer require spomky-labs/otphp 安装;TOTP 输出固定因密钥、时间、参数未变,应使用 Otp::generateSecret() 生成 Base32 密钥;生成兼容码须确保密钥 Base32 编码、period=30、digits=6,并用 getProvisioningUri() 构造含 issuer 和 label 的标准 otpauth URI。

Composer 安装 otp-php 失败或找不到包?
otp-php 并非 Packagist 官方注册的标准 Composer 包,它的正式名称是 spomky-labs/otphp。直接运行 composer require otp-php 会报错:Could not find package otp-php。
- 必须使用其真实命名空间安装:
composer require spomky-labs/otphp - 该库由 Spomky-Labs 维护,支持 TOTP(基于时间)和 HOTP(基于计数),文档和更新较活跃
- 如果你误用了旧版 fork(如
robthree/twofactorauth),它不提供纯 OTP 生成器,而是完整认证框架,引入后体积大、依赖多,且 TOTP 实现细节不同
生成 TOTP 密钥时为什么每次结果都一样?
TOTP 的核心是密钥 + 时间窗口 + 算法参数。若密钥固定、时间未变、参数一致,则输出必然相同——这本身不是 bug,而是设计使然。但实际开发中容易因以下原因误判:
-
$secret被硬编码为字符串(如"SECRET"),而非 Base32 编码的随机字节。正确做法是用Otp::generateSecret()生成 10 字节以上随机密钥,再 Base32 编码存储 - 时间同步偏差:服务器与客户端时间差超过 30 秒(默认窗口)会导致验证失败。调试时可用
date('U')对比双方 UNIX 时间戳 - 使用了错误的哈希算法:默认是
sha1,但某些客户端(如 Google Authenticator)也支持sha256或sha512。生成和验证必须用同一算法,否则结果不匹配
如何用 otphp 生成一个标准兼容的 TOTP 码?
关键在于初始化 TOTP 实例时传入正确的参数组合,否则生成的码无法被 Authy、Google Authenticator 等识别:
- 密钥必须是 Base32 编码字符串(不含空格、小写),例如
"JBSWY3DPEHPK3PXP";不能直接传 raw binary 或 hex 字符串 - 时间步长(
$period)默认为 30 秒,必须与客户端一致;若改用 60 秒,需在二维码 URI 中显式声明&period=60 - 代码长度(
$digits)通常为 6,部分银行应用要求 8,需和服务端验证逻辑对齐 - 示例:
use OTPHP\TOTP; </li></ul><p>$totp = new TOTP('JBSWY3DPEHPK3PXP'); $code = $totp->now(); // 返回当前 6 位数字字符串</p>生成二维码时 missing issuer 或 label 导致扫码失败?
Authenticator 应用解析二维码时依赖 URI 结构。仅靠密钥不够,必须构造合规的
otpauth://totp/URI:立即学习“PHP免费学习笔记(深入)”;
-
issuer参数决定 App 内显示的组织名(如 “MyApp”),缺失时部分客户端会忽略或显示异常 -
label是账户标识,格式应为issuer:username(如MyApp:alice@example.com),冒号不可省略 - Base32 密钥需 URL-safe 编码(即去掉
=填充,替换+和/) - 可用
$totp->getProvisioningUri()自动生成完整 URI,它已内置处理 issuer、label、algorithm、digits、period 等字段
真正麻烦的不是生成代码本身,而是密钥分发、时间同步、URI 编码细节和客户端兼容性测试——这些环节出错,前端扫出来的码永远对不上。
-



















