淘宝网站开发成本,适合网络营销的产品,网页构建器,怎么做网站图片链接前言
在 401 问题上卡了 一段时间#xff0c;参考官网文档和鉴权签名计算测试也试了很久#xff0c;签名确定是没错的#xff0c;但是一直提示 INVALID_SIGNATURE
其实问题在于我忽略了 公共请求头格式 中 Content-MD5 部分的一句话#xff1a; GET 和 DELETE 请求且 Bod…前言
在 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($this, [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 GET) {$content_type ;} else if ($method POST) {if (str_starts_with($content_type, application/json)) {$body json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);}// 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 客户端如果使用 Guzzlesend 换成 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 需根据实际业务调整