thinkphp 6 中生成标准 rsa 签名需按 key 字典序排序、rawurlencode 所有 value、拼接为 a=b&c=d 形式,再用 sha256 摘要和 openssl_sign() 签名;验签前必须用相同规则还原原始串,密钥应缓存 resource 而非字符串,时间戳须校验 ±300 秒误差。

ThinkPHP 6 中如何生成标准 RSA 签名(sign)
签名不是拼接字符串再 md5() 就完事——必须用私钥对规范化的请求参数做 openssl_sign(),否则前端/第三方调用必验签失败。
关键点:参数必须按 key 字典序排序、URL 编码、拼成 a=b&c=d 形式,再用 SHA256 摘要 + OPENSSL_ALGO_SHA256 签名。ThinkPHP 自身不封装这层逻辑,得自己写。
-
ksort()后过滤掉sign、timestamp(若用于防重放)等非业务字段 - 所有 value 必须
rawurlencode(),不是urlencode()(后者会把空格转成+) - 私钥需 PEM 格式且带头尾标记(
-----BEGIN PRIVATE KEY-----),直接读文件内容传给openssl_pkey_get_private() - 签名结果用
base64_encode()输出,不是 hex 或 raw binary
ThinkPHP 接口验签时为什么总是 openssl_verify() === false
90% 的情况不是密钥错了,而是验签前的数据没“还原”成签名时的原始串——服务端收到的请求可能是 JSON body、Query 参数混用、或被框架自动 decode 过。
比如前端发 {"a":"x y","b":"z"},ThinkPHP $request->param() 返回的是已 urldecode 的数组,但签名时用的是原始 a=x%20y&b=z,两者不一致必然失败。
- 验签前必须用相同规则重新构造原始字符串:取
$request->param()但剔除sign,再ksort+rawurlencode拼接 - 别用
$request->post()或$request->get()单独取,它们可能丢失部分参数或自动转义 - 公钥格式必须和私钥配对,PEM 公钥开头是
-----BEGIN PUBLIC KEY-----,不是-----BEGIN RSA PUBLIC KEY-----(后者是 PKCS#1,PHP 8.0+ 默认不支持) - 验签函数第三个参数要用
OPENSSL_ALGO_SHA256,不能省略或错写成'sha256'字符串
如何在 ThinkPHP 中安全加载和缓存 RSA 密钥
每次验签都 file_get_contents() 读私钥/公钥文件?磁盘 IO 和解析开销不小,尤其高并发下容易成为瓶颈;硬编码进配置?密钥泄露风险极高。
推荐方案:用 ThinkPHP 的 Cache 组件缓存解析后的密钥资源(openssl_pkey_get_private 返回的 resource),而非字符串本身——resource 无法序列化,但可以封装成单例对象复用。
- 密钥文件放在
config/外(如runtime/keys/),避免被 Web 目录直接访问 - 首次加载时用
openssl_pkey_get_private($pem)解析,成功后存入think\Container单例,后续直接 get - 不要用
define()或全局变量存密钥内容,防止被phpinfo()或错误日志意外输出 - 开发环境可用
.env控制是否启用签名,但线上必须强制开启,避免漏配
时间戳校验与防重放攻击的实际落地要点
只做签名不校验时间戳,攻击者抓包重放一次有效请求就能绕过全部安全机制——哪怕签名正确,也得拒绝 5 分钟前的请求。
注意:客户端和服务端时间差必须可控,不能直接比对 time() 和请求里的 timestamp,要允许 ±300 秒误差,且误差值应可配置。
- 客户端 timestamp 必须是秒级 Unix 时间戳(
Math.floor(Date.now()/1000)),不是毫秒 - 服务端拿到后先
abs($timestamp - time()) > $allowed_skew,超限直接返回 401,不再走验签流程 - 别把
timestamp放进签名原文里却不校验它——这是常见逻辑断层:签了却不用,形同虚设 - 如果接口支持批量操作,每个子项仍需独立验签或统一用批次 timestamp,不能只验外层
签名验证真正难的不是算法,而是参数归一化、密钥生命周期管理、以及时间窗口和并发场景下的状态一致性。这些地方出问题,日志里通常只显示 “验签失败”,根本看不出是拼接顺序错了,还是 base64 解码时丢了换行符。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










