ThinkPHP8接口签名验证必须在控制器前由中间件拦截,支持GET/POST/JSON请求,需剔除sign等字段后按字典序拼接参数并结合路径、密钥生成SHA256签名,同时校验timestamp(≤180秒)和nonce(Redis防重放)。

ThinkPHP8接口签名验证必须在请求进入控制器前完成拦截,否则恶意参数可能已触发日志写入或数据库操作;签名中间件要能同时处理GET、POST、JSON三种请求体,且必须剔除sign、timestamp、nonce等非业务字段后再参与验签计算。
注册签名中间件并设置生效范围
打开app/middleware.php文件,在全局中间件数组中插入自定义中间件类路径,注意顺序:必须排在跨域中间件之后、业务控制器之前。
在app/middleware.php中添加:['app\middleware\CheckSign']到$middleware数组靠前位置,但确保不在TrustHosts::class之前。
若只对API路由启用,改用分组注册方式:在app/route/app.php中,用Route::group('api', function () { ... })->middleware(CheckSign::class);包裹所有API路由。
【务必避开静态资源路径】——中间件若注册为全局,会拦截/favicon.ico、/public/**等请求,导致前端报401错误。
编写CheckSign中间件核心逻辑
在app/middleware/CheckSign.php中创建类,handle方法内执行三步关键操作:
第一步:提取原始请求数据。调用$request->getInput()获取原始输入流,不依赖$request->param()(该方法对JSON请求体返回空数组)。
第二步:按Content-Type解析参数。若$request->header('content-type')包含application/json,用json_decode($input, true)解析;否则用parse_str($input, $bodyData);再与$request->get()合并:array_merge($request->get(), $bodyData)。
第三步:剥离签名相关字段。从合并后的参数数组中移除sign、sign_version、timestamp、nonce、app_id(若存在),未剔除会导致签名原文循环依赖,验签必然失败。
构造签名原文并比对哈希值
方法一:标准字典序拼接法
对剥离后的参数数组执行ksort($params),确保键名ASCII升序排列;遍历数组,每个value先trim()再rawurlencode()(不能用urlencode(),它把空格转+号,客户端常用encodeURIComponent对应rawurlencode);拼成key1=value1&key2=value2格式字符串。
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
方法二:路径+参数+密钥串联法
签名原文=接口路径($request->url(true)不含域名和query)+上一步拼出的参数字符串+&sign_version=1+配置中的密钥(config('app.api_secret'));最终用hash_hmac('sha256', $signString, $secret)生成服务端签名。
【客户端和服务端必须用完全相同的签名原文】——哪怕多一个空格、少一个斜杠、中文编码不一致(都需UTF-8)、BOM字符残留,哈希值就不同。
时间戳与nonce双重防重放
1. 读取请求头或参数中的timestamp字段,强制转为整型:(int)$params['timestamp']。
2. 计算时间差:abs(time() - $clientTs),超过180秒直接拒绝,不进后续验签流程。
3. 读取nonce字段,构造Redis key:sign:nonce:.$params['app_id'].:.$params['nonce'],用Redis::setex()写入并设TTL为300秒;若key已存在,说明是重放请求,立即中断。
这一步必须在签名比对前执行——防止攻击者反复提交同一套参数+新nonce绕过时间校验。
调试时输出签名原文对比
在中间件handle方法末尾添加临时调试逻辑:if ($request->param('debug_sign') === '1') { Log::write("SIGN_RAW: {$signString}", 'debug'); }。
客户端同步打印自己构造的签名原文,逐字符比对两端字符串是否完全一致——90%验签失败问题根源在此。
线上环境必须删除或注释该行,避免密钥和参数泄露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










