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 签宝接口签名指南
前言 在 401 问题上卡了 一段时间,参考官网文档和鉴权签名计算测试也试了很久,签名确定是没错的,但是一直提示 INVALID_SIGNATURE 其实问题在于我忽略了 公共请求头格式 中 Content-MD5 部分的一句话: GET 和 DELETE 请求且 Bod…...
穿越Flink的时间隧道:解锁实时数据之窗,掌握流处理之巅
目录 Flink中的时间和窗口 1时间语义 1.1Flink中的时间语义 1.1.1处理时间 1.1.2事件时间 1.2那种时间语义更重要 2 水位线 2.1 事件时间和窗口 2.2 什么是水位线 2.3 如何生成水位线 2.3.1使用WatermarkGenerator 2.3.2使用SourceFunction 2.4 水位线的传递 2.5 水位…...
服务器与Ajax
1.初识Ajax Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。 术语ajax最早产生于2005年,Ajax表示Asynchronous JavaScript and XML(异步JavaScript和XML),但是它不是像HTML、JavaScript或CSS这样的一种“正式的”技…...
Electron项目架构方案心得
这里只记录FE的业务流程设计考虑和Electron的业务流程考虑。 一个electron客户端,理想的架构是,底层的能力支持完全由electron底层模块支持。关于electron底层模块的支持,不限于node packages 或者 dll 或者 rust实现的一些东东。而业务流程…...
Java中创建List接口、ArrayList类和LinkedList类的常用方法(一)
List接口 要了解List接口,就不得不说起Java的集合框架。 (该图来自菜鸟教程) Collection接口和Map接口 Java 集合框架主要包括两种类型的容器,集合Collection和图Map。 Collection接口代表了单列集合,它包含了一组…...
顶级开源社区开发者体验实践分享
实践一:开发者贡献指南 实践二:Dev Container Setup(bonus) 实践三:Good First Issues 实践三:Good Second Issues(bonus) 实践四:RFC 机制 实践五:完善 CI I…...
STM32之RTC实时时钟
一、实时时钟概述 1、实时时钟介绍 英文缩写:RTC。显示年、月、日、时、分、秒、星期,自动计算闰年,能够区分每个月的天数。 RTC特点:能从RTC获取到具体的日期时间,断掉后再开机时间仍然准确(需要纽扣电池ÿ…...
Java JVM 堆、栈、方法区详解
目录 1. 栈 2. 堆 3. 方法区 4. 本地方法栈 5. 程序计数器 首先来看一下JVM运行时数据区有哪些。 1. 栈 在介绍JVM栈之前,先了解一下 栈帧 概念。 栈帧:一个栈帧随着一个方法的调用开始而创建,这个方法调用完成而销毁。栈帧内存放者方…...
Oracle篇—分区表和分区索引的介绍和分类(第一篇,总共五篇)
☘️博主介绍☘️: ✨又是一天没白过,我是奈斯,DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux,也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注❣…...
Vue中的模式和环境变量
文章目录 一、介绍二、配置1、环境文件2、变量使用 三、读取环境文件 一、介绍 vue官网:https://cli.vuejs.org/zh/guide/mode-and-env.html模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式 开发环境:develop…...
用ChatGPT教学、科研!亚利桑那州立大学与OpenAI合作
亚利桑那州立大学(简称“ASU”)在官网宣布与OpenAI达成技术合作。从2024年2月份开始,为所有学生提供ChatGPT企业版访问权限,主要用于学习、课程作业和学术研究等。 为了帮助学生更好地学习ChatGPT和大语言模型产品,AS…...
问题解决:django模型查询报错,找不到数据库表
django项目,使用的postgresql数据库,建了多个模式,模型查询时一直默认查public的表 1. 问题: django.db.utils.ProgrammingError: relation "ip_management_app.table" does not exist 2. 代码: class …...
持续集成工具Jenkins的使用之安装篇(一)
Jenkins是一个基于Java开发的开源的一种持续集成工具,主要用于环境部署,监控重复性的工作,旨在提供一个开放易用的软件平台,使软件项目可以进行持续集成。要想使用它,你就必须的先安装,接下来我们就介绍下J…...
【JavaScript】面向后端快速学习 笔记
文章目录 JS是什么?一、JS导入二、数据类型 变量 运算符三、流程控制四、函数五、对象 与 JSON5.1 对象5.2 JSON5.3 常见对象1. 数组2. Boolean对象3. Date对象4. Math5. Number6. String 六、事件6.1 常用方法1. 鼠标事件2. 键盘事件3. 表单事件 6.2 事件的绑定**1…...
笨蛋学设计模式行为型模式-命令模式【19】
行为型模式-命令模式 8.6命令模式8.6.1概念8.6.2场景8.6.3优势 / 劣势8.6.4命令模式可分为8.6.5命令模式8.6.6实战8.6.6.1题目描述8.6.6.2输入描述8.6.6.3输出描述8.6.6.4代码 8.6.7总结 8.6命令模式 8.6.1概念 命令模式允许将请求封装成一个对象(命令对象,包含…...
windows用msvc编译opencv、opencv-python、opencv_contrib、cuda
如要用mingw编译opencv,参考我另外一篇文章https://blog.csdn.net/weixin_44733606/article/details/135741806。 如要用Ubuntu编译opencv,参考我另外一篇文章https://blog.csdn.net/weixin_44733606/article/details/131720128。 一、安装VS2022&…...
JVM实战篇:GC调优
目录 一.GC调优的核心指标 1.1吞吐量(Throughput) 1.2延迟(Latency) 1.3内存使用量 二.GC调优的方法 2.1监控工具 Jstat工具 VisualVm插件 Prometheus Grafana 2.2诊断原因 GC日志 GC Viewer GCeasy 2.3常见的GC模…...
C# 获取QQ会话聊天信息
目录 利用UIAutomation获取QQ会话聊天信息 效果 代码 目前遇到一个问题 其他解决办法 利用UIAutomation获取QQ会话聊天信息 效果 代码 AutomationElement window AutomationElement.FromHandle(get.WindowHwnd); AutomationElement QQMsgList window.FindFirst(Tr…...
Java中的内存溢出与内存泄漏深度解析
目录 引言 一. 内存溢出(Memory Overflow) 1.1 堆内存溢出 1.2 栈内存溢出 1.3 内存溢出的解决策略 1.3.1 优化对象的创建和销毁 1.3.2 调整堆内存大小 1.3.3 使用内存分析工具 1.3.4 避免创建过大的对象 1.3.5 定期清理不再使用的对象 二、…...
计算机网络安全——密码学入门
网络安全是指在网络领域、专业领域的网络安全包括在基础计算机网络基础设施中所做的规定,网络管理员采取的策略来保护网络及网络可访问资源免受未经授权的访问,以及对其有效性(或缺乏)的持续不断的监控和测量的结合。 1. 密码学的…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
