主页 > 手机  > 

PHP调用e签宝接口签名指南


前言

在 401 问题上卡了 一段时间,参考官网文档和鉴权签名计算测试也试了很久,签名确定是没错的,但是一直提示 INVALID_SIGNATURE

其实问题在于我忽略了 公共请求头格式 中 Content-MD5 部分的一句话:

GET 和 DELETE 请求且 Body 体无数据时,此参数可为 “”(空字符串)或不传此参数。

因为参数必选部分他写了 否,我就只关注这个了…害

鉴权签名计算:https://open.esign.cn/tools/signature

下面就快速列出代码了

代码部分 获取 Content-MD5 /** * @param string $body * * @return string 请求体字符串,如果是文件则需要 md5_file 方法并传入文件名(带路径) */ public function getContentMd5(string $body): string { return base64_encode(md5($body, true)); } 获取签名

这里我将 App Secret 直接作为形参传入了。因为请求头和 Date 可忽略,这里也直接不作处理。

/** * @param string $method * @param string $content_md5 * @param string $content_type * @param string $uri * @param string $app_secret * * @return string */ public function getSignature(string $method, string $content_md5, string $content_type, string $uri, string $app_secret): string { $string = "$method\n*/*\n$content_md5\n$content_type\n\n$uri"; return base64_encode(hash_hmac('sha256', $string, $app_secret, true)); } 构建请求头

需注意 X-Tsign-Open-Ca-Timestamp 请求头 必需 传入毫秒级时间戳,也就是 13 位长,用 time() 方法获取的是秒级,同样会得到 401 INVALID_TIMESTAMP 的响应

/** * @param string $app_id * @param string $app_secret * @param string $method * @param string $body 这里以 JSON 作为请求体示例,如果涉及到其它类型请求,自行修改一下 * @param string $content_type * @param string $uri * * @return array */ public function buildSignedHeaders(string $app_id, string $app_secret, string $method, string $body, string $content_type, string $uri): array { $contentMd5 = ''; if (in_array($method, ['GET', 'DELETE'])) { $content_type = ''; } else { $contentMd5 = $this->getContentMd5($body); } return [ 'Accept' => '*/*', 'Content-MD5' => $contentMd5, 'Content-Type' => $content_type, 'X-Tsign-Open-App-Id' => $app_id, 'X-Tsign-Open-Auth-Mode' => 'Signature', 'X-Tsign-Open-Ca-Signature' => $this->getSignature($method, $contentMd5, $content_type, $uri, $app_secret), 'X-Tsign-Open-Ca-Timestamp' => Carbon::now()->getTimestampMs() ]; }

如果没有 Carbon 库,可以用官方 Demo 的写法:

/** * @return float 返回值是个 double */ public function getMillisecond(): float { [$t1, $t2] = explode(' ', microtime()); return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000); } 发送请求 /** * @param string $method * @param string $uri * @param array $data * @param string|null $content_type * @param bool $sandbox * * @return array|null */ public function request(string $method, string $uri, array $data = [], ?string $content_type = 'application/json', bool $sandbox = false): ?array { $method = strtoupper($method); // 统一转换为大写 $body = ''; if ($method === 'POST') { if (str_starts_with($content_type, 'application/json')) { $body = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } else { // TODO: 其它类型的接口自行处理 } } // 此处通过 .env 文件取配置 $host = env('ESIGN_HOST'); // https://openapi.esign.cn $appId = env('ESIGN_APPID'); $appSecret = env('ESIGN_APPSECRET'); // 可以根据参数进入沙盒环境方便调试 if ($sandbox) { $host = env('ESIGN_SANDBOX_HOST'); // https://smlopenapi.esign.cn $appId = env('ESIGN_SANDBOX_APPID'); $appSecret = env('ESIGN_SANDBOX_APPSECRET'); } $headers = $this->buildSignedHeaders($appId, $appSecret, $method, $body, $content_type, $uri); // 这里使用了 Laravel/Lumen 9+ 的内置 Http Facade,实际也是调用 GuzzleHttp 客户端,如果直接使用 Guzzle 的 Client,send 换成 request 即可 // 可能有人会问为什么不直接用定义好的 asJson 和 post 方法,因为...他带上了 UA,得重新处理签名部分,我懒 $response = Http::send($method, $host . $uri, [ 'headers' => $headers, 'body' => $body // 示例仅针对 JSON 请求,其它接口需调整 ])->body(); $response = json_decode($response, true); if (json_last_error() === JSON_ERROR_NONE) { return $response; } return null; } 调用

假设类名为 ESignService

// 所以 Carbon 这个库真的方便 ;) $from= Carbon::createFromDate(2023, 12, 1)->startOfDay()->getTimestampMs(); $to = Carbon::createFromDate(2023, 12, 31)->endOfDay()->getTimestampMs(); // 此处为查询集成方企业流程列表接口 var_dump((new ESignService)->request('POST', '/v3/organizations/sign-flow-list', [ 'pageNum' => 1, 'pageSize' => 10, 'signFlowStartTimeFrom' => $from, // 对于这个接口,signFlowStartTimeFrom 和 signFlowStartTimeTo 是必传的,文档上必选为否又误导了 'signFlowStartTimeTo' => $to ]));

注意: 部分接口形式带有资源路由参数,如 /v3/sign-flow/{signFlowId}/preview-file-download-url,上方代码仅供参考,中间的 signFlowId 需根据实际业务调整

标签:

PHP调用e签宝接口签名指南由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“PHP调用e签宝接口签名指南