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. 密码学的…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
