前言
在 401 問題上卡了 一段時間,參考官網(wǎng)文檔和鑒權(quán)簽名計算測試也試了很久,簽名確定是沒錯的,但是一直提示 INVALID_SIGNATURE
其實問題在于我忽略了 公共請求頭格式 中 Content-MD5
部分的一句話:
GET 和 DELETE 請求且 Body 體無數(shù)據(jù)時,此參數(shù)可為 “”(空字符串)或不傳此參數(shù)。
因為參數(shù)必選部分他寫了 否,我就只關(guān)注這個了…害
鑒權(quán)簽名計算: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));
}
構(gòu)建請求頭
需注意 X-Tsign-Open-Ca-Timestamp
請求頭 必需 傳入毫秒級時間戳,也就是 13 位長,用 time()
方法獲取的是秒級,同樣會得到 401 INVALID_TIMESTAMP
的響應(yīng)
/**
* @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);
}
發(fā)送請求
/**
* @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); // 統(tǒng)一轉(zhuǎn)換為大寫
$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');
// 可以根據(jù)參數(shù)進(jìn)入沙盒環(huán)境方便調(diào)試
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+ 的內(nèi)置 Http Facade,實際也是調(diào)用 GuzzleHttp 客戶端,如果直接使用 Guzzle 的 Client,send 換成 request 即可
// 可能有人會問為什么不直接用定義好的 asJson 和 post 方法,因為...他帶上了 UA,得重新處理簽名部分,我懶
$response = Http::send($method, $host . $uri, [
'headers' => $headers,
'body' => $body // 示例僅針對 JSON 請求,其它接口需調(diào)整
])->body();
$response = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE) {
return $response;
}
return null;
}
調(diào)用
假設(shè)類名為 ESignService
文章來源:http://www.zghlxwxcb.cn/news/detail-807942.html
// 所以 Carbon 這個庫真的方便 ;)
$from= Carbon::createFromDate(2023, 12, 1)->startOfDay()->getTimestampMs();
$to = Carbon::createFromDate(2023, 12, 31)->endOfDay()->getTimestampMs();
// 此處為查詢集成方企業(yè)流程列表接口
var_dump((new ESignService)->request('POST', '/v3/organizations/sign-flow-list', [
'pageNum' => 1,
'pageSize' => 10,
'signFlowStartTimeFrom' => $from, // 對于這個接口,signFlowStartTimeFrom 和 signFlowStartTimeTo 是必傳的,文檔上必選為否又誤導(dǎo)了
'signFlowStartTimeTo' => $to
]));
注意: 部分接口形式帶有資源路由參數(shù),如 /v3/sign-flow/{signFlowId}/preview-file-download-url
,上方代碼僅供參考,中間的 signFlowId
需根據(jù)實際業(yè)務(wù)調(diào)整文章來源地址http://www.zghlxwxcb.cn/news/detail-807942.html
到了這里,關(guān)于PHP 調(diào)用 e 簽寶接口簽名指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!