ThinkPHP用二级域名实现API签名验证的核心是让子域名成为独立安全上下文载体,支持专属密钥、限流规则和验签中间件隔离;需启用url_domain_deploy、透传Host头、显式注册子域名路由;动态加载子域名对应密钥;签名原文拼接须路径+排序后urlencode参数+sign_version+密钥;风控策略按子域名细粒度隔离。

ThinkPHP用二级域名实现API签名验证,核心不是把验签逻辑“搬到子域名下”,而是让子域名成为独立安全上下文的载体——每个子域名可配置专属密钥、限流规则和验签中间件,避免不同业务线共用同一套签名策略导致风险扩散。
一、先让子域名能被框架准确识别
签名验证依赖请求来源的精确判断,必须确保子域名信息完整透传:
- 在config/app.php中启用域名部署:
'url_domain_deploy' => true(注意不是APP_SUB_DOMAIN_DEPLOY) - Nginx/Apache需透传
Host头,禁止反向代理时覆盖或丢弃;本地开发不能用php -S,必须用真实Web服务器 - 若用
api.example.com承载API,需在route/app.php中显式注册:Route::domain('api.example.com', function () { /* API路由 */ });
二、为每个子域名配独立签名密钥与中间件
不同子域名代表不同业务域,密钥和验签规则应物理隔离:
- 在config/api.php(或按子域名建config/api.example.com.php)中定义
'sign_key' => 'xxx',避免硬编码在中间件里 - 创建专用中间件app/middleware/ApiSignVerify.php,在app/middleware.php中仅对
api.example.com路由启用 - 中间件内通过
$request->host()提取子域名,动态加载对应密钥:Config::get('api.sign_key_' . $subDomain)
三、签名原文拼接必须严格一致
90%验签失败源于服务端拼串和客户端不一致,子域名场景下更要统一规则:
立即学习“PHP免费学习笔记(深入)”;
- 路径只取
$request->url(true)(如/v1/order/create),剔除域名、query参数、index.php - 参数合并顺序:先
$request->get(),再根据Content-Type读php://input并解析JSON或form-data,最后array_merge - 剔除
sign、signature等字段后,对剩余参数ksort(),每个$v单独rawurlencode(),拼成k1=v1&k2=v2格式 - 最终签名原文 =
路径 + & + 参数串 + &sign_version=1 + rawurlencode(密钥)
四、配合子域名做细粒度风控
子域名天然具备隔离性,可叠加防重放、限频等策略:
- 时间戳校验容差设为300秒,但
timestamp必须从请求参数中读,不可用time()直接比 - nonce缓存键加子域名前缀:
Cache::set("nonce:{$subDomain}:{$nonce}", 1, 300),防止跨域名复用 - 每分钟调用量限制也按子域名+app_id双维度计数:
send_limit:{$subDomain}:{$appId}:minute



















