当前位置: 首页 > news >正文

PHP实现一个简单的接口签名方法以及思路分析

文章目录

  • 签名生成说明
  • 签名生成示例代码
  • 签名校验示例代码

签名生成说明

B项目需要调用A项目的接口,由A项目为B项目分配 AccessKeySecretKey,用于接口加密,确保不易被穷举,生成算法不易被猜测。

最终需要确保包含签名的参数只能被有效的请求一次,重复请求则视为无效参数;并且设定参数有效时长(例如5分钟),超时则视为无效参数。

AccessKey 和 SecretKey分配:

测试环境:
ACCESS_KEY = test_access
SECRET_KEY = test_secret正式环境:(另行配置)

假设A项目和B项目通过json格式传递参数,在PHP中对请求的json参数转化为数组,然后对原本的请求参数追加如下字段值:

  • AccessKey:已分配的请求key,固定值;
  • timestamp:当前毫秒时间戳;
  • nonce:唯一随机10位字符串,15分钟内不允许重复;

例如,原本的请求参数 $params 为:

Array
([ToUserName] => wxdd5624bd15b1691a[FromUserName] => sys[CreateTime] => 1717554600[MsgType] => event[Event] => sys_approval_change[AgentID] => 1000043
)

$params 追加 AccessKeytimestampnonce 之后:

Array
([ToUserName] => wxdd5624bd15b1691a[FromUserName] => sys[CreateTime] => 1717554600[MsgType] => event[Event] => sys_approval_change[AgentID] => 1000043[AccessKey] => test_access[timestamp] => 1717659814771[nonce] => 6bc6f34969
)

$params 的 key 值按照字母升序排列(PHP中的 ksort 函数):

Array
([AccessKey] => test_access[AgentID] => 1000043[CreateTime] => 1717554600[Event] => sys_approval_change[FromUserName] => sys[MsgType] => event[ToUserName] => wxdd5624bd15b1691a[nonce] => 756c577626[timestamp] => 1717659831355
)

然后,将上述参数赋给一个临时的变量(例如:$tmp_params),并且拼接 SecretKey,然后整体json_encode,再次md5之后,得到sign值,代码如下:

$sign = md5(json_encode($tmp_params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

sign值 追加到 $params 参数中(注意:是$params参数,不是 $tmp_params ),最终参数如下:

Array
([AccessKey] => test_access[AgentID] => 1000043[CreateTime] => 1717554600[Event] => sys_approval_change[FromUserName] => sys[MsgType] => event[ToUserName] => wxdd5624bd15b1691a[nonce] => 137c128684[timestamp] => 1717660145228[sign] => ff0ea47d561eb2d9735771f0bc85ad33
)

将上述参数转化为json后作为最终的请求参数:

{"AccessKey": "test_access","AgentID": "1000043","CreateTime": "1717554600","Event": "sys_approval_change","FromUserName": "sys","MsgType": "event","ToUserName": "wxdd5624bd15b1691a","nonce": "fb212b7327","timestamp": 1717660335729,"sign": "9e5321b10ddc975b89a228e94d8e5f04"
}

签名生成示例代码

public function createSign()
{$mock_json = '{"ToUserName": "wxdd5624bd15b1691a","FromUserName": "sys","CreateTime": "1717554600","MsgType": "event","Event": "sys_approval_change","AgentID": "1000043"}';$params = json_decode($mock_json, true);//对原本的请求参数追加如下字段值:$params['AccessKey'] = 'test_access'; //已分配的请求key,固定值$params['timestamp'] = intval(microtime(true) * 1000); //当前毫秒时间戳$params['nonce'] = substr(uniqid(), -6) . rand(1000, 9999); //唯一随机10位字符串,15分钟内不允许重复//按照上述所有请求参数的key值的字母升序排列(PHP中的 `ksort` 函数):ksort($params);//然后,将上述参数赋给一个临时的变量,并且拼接 SecretKey, 然后整体json_encode,再次md5之后,得到sign值$tmp_params = $params;$tmp_params['SecretKey'] = 'test_secret';$sign = md5(json_encode($tmp_params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));//将 sign值 追加到 $params 参数中(注意:是$params参数,不是 $tmp_params )$params['sign'] = $sign;//将上述参数转化为json后作为最终的请求参数:echo json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

签名校验示例代码

<?phpclass Demo
{//时间常量const TIME_OUT = 300; //超时时间  5分钟const NONCE_INTERVAL = 900;   //允许nonce时间间隔   15分钟/*** 签名验证* @param $params array 客户端请求来的原本的参数数组* @return array* @throws \Exception*/public function checkSign($params){$request_params = $params;if (empty($params['timestamp']) || empty($params['nonce']) || empty($params['sign'])) {throw new \Exception('签名基础参数校验失败', 201);}//校验超时$timestamp = intval($params['timestamp'] / 1000);if (abs(time() - $timestamp) > self::TIME_OUT) {throw new \Exception('请求参数已超时', 201);}//从配置文件中读取ACCESS_KEY和SECRET_KEY$access_key = env('ACCESS_KEY');$secret_key = env('SECRET_KEY');if (empty($access_key) || empty($secret_key)) {throw new \Exception('NEW_CRM_REQUEST配置异常', 201);}if ($access_key != $params['AccessKey']) {throw new \Exception('无效的AccessKey', 201);}$nonce_key = 'test_nonce:' . $params['timestamp'] . '_' . $params['nonce'];$exist_nonce = RedisUtils::init()->get($nonce_key);if ($exist_nonce) {throw new \Exception('无效的nonce值', 201);}$sign = $params['sign'];unset($params['sign']);ksort($params);$params['SecretKey'] = $secret_key;$params_json = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);$params_sign = md5($params_json);if ($params_sign != $sign) {//todo 写入错误log 或 发送报警信息//todo 校验频繁请求失败的IP,可以考虑将这些IP加入黑名单throw new \Exception('签名校验失败', 201);}RedisUtils::init()->set($nonce_key, 1, self::NONCE_INTERVAL);unset($params['AccessKey']);unset($params['SecretKey']);unset($params['nonce']);unset($params['timestamp']);return $params;}
}

最终效果,同样的请求参数如果被抓包,再次请求就会失败:
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 单片机(STM32)与上位机传输浮点数
  • 20240606更新Toybrick的TB-RK3588开发板在Android12下的内核
  • kafka-生产者事务-数据传递语义事务介绍事务消息发送(SpringBoot整合Kafka)
  • 个人博客搭建
  • Android 蓝牙profile的配置
  • [数据集][图像分类]人种黄种人白人黑人等分类数据集56000张7类别
  • 【Oracle】Oracle导入导出dmp文件
  • 【Golang】Go语言中defer与return的精妙交织:探索延迟执行与返回顺序的微妙关系
  • Webpack 从入门到精通-基础篇
  • Linux网络命令——tcpdump
  • 高考分数查询结果自动推送至微信(卷II)
  • 电脑录屏软件哪个好用视频最清晰
  • 损失函数(Loss Function)
  • RainBond 制作应用并上架【以ElasticSearch为例】
  • JDK8特性学习笔记
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【剑指offer】让抽象问题具体化
  • 2017 年终总结 —— 在路上
  • Consul Config 使用Git做版本控制的实现
  • exif信息对照
  • JavaScript新鲜事·第5期
  • Mocha测试初探
  • SpiderData 2019年2月23日 DApp数据排行榜
  • spring学习第二天
  • Zsh 开发指南(第十四篇 文件读写)
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 动态规划入门(以爬楼梯为例)
  • 基于游标的分页接口实现
  • 判断客户端类型,Android,iOS,PC
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 三栏布局总结
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 使用权重正则化较少模型过拟合
  • 听说你叫Java(二)–Servlet请求
  • 一些css基础学习笔记
  • 异步
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​2021半年盘点,不想你错过的重磅新书
  • #AngularJS#$sce.trustAsResourceUrl
  • (1)常见O(n^2)排序算法解析
  • (C++17) optional的使用
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (第27天)Oracle 数据泵转换分区表
  • (二)测试工具
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (十) 初识 Docker file
  • (循环依赖问题)学习spring的第九天
  • (原創) 物件導向與老子思想 (OO)
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (转)重识new
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET Core中的时区转换问题
  • .NET框架类在ASP.NET中的使用(2) ——QA