对接第三方服务引起的小思考-回调和Sign算法
背景
最近在对接一个同事写的支付公用模块,然后对第三方服务引起一两个小思考。
思考
回调
来看看我们同事是如何做回调的。
首先,请求支付接口的时候,将回调URL作为请求body的一个参数[不加密]。
{
"xxx": "xxx",
"xxx": "xx",
"xxxxx": "xxxxx",
"callBackUrl": "http://www.baidu.com"
}
然后,当第三方支付服务成功后,支付服务会对上面的回调URL发出一次http请求,然后固定请求体带上以下参数:
{
"code": 0,
"充值订单号": "3333333333333333",
"支付订单号": 361504175005106176,
"支付状态": "SUCCESS",
"充值数额": 88.00,
"subject": "游戏",
"gmtCreate": "2019-08-21 10:49:21",
"gmtPayment": "2019-08-21 10:49:21",
}
到这里,我们可以看到
1、回调url是没有加密的,那么如果有黑客拦截到此数据,就可以拿到此url不断地攻击服务器了。
2、回调时的参数是支付服务方固定的,而且没有做到扩展,调用方不能增加自定义参数。
那么比较好的解决方案有什么呢,我们可以参考一下阿里的对象存储OSS是怎么做的。
Callback
用户只需要在发送给 OSS 的请求中携带相应的 Callback 参数,即能实现回调。
Callback包含下面字段(json格式):
{
"callbackUrl":"121.101.166.30/test.php", // 回调url
"callbackHost":"oss-cn-hangzhou.aliyuncs.com", // 回调host
"callbackBody":"{\"mimeType\":${mimeType},\"size\":${size}}", //回调请求体
"callbackBodyType":"application/json" // 回调请求体类型
}
然后将Callback对象转成Json字符串,再进行Base64编码,最后将Base64编码后的字符串作为一个请求参数即可。最后的请求体可能为如下:
{
"xxx": "xxx",
"xxx": "xx",
"xxxxx": "xxxxx",
"callBack": "23jdf7adf8gfasg98g78a9dgda"
}
这样的话,我们可以看到,这就算是被别人拦截了,第一眼看过去肯定是懵逼的。但是呢,只要猜到是base64编码的,反编码后还是可以看到里面的东西。如果对安全性的要求是极致的,大家还可以加上加密算法[可解密],千万不要加密完不能倒退回来。。。
Sign算法
一般的Sign算法,将请求参数以key1=value2&key2=value2的格式拼接起来,然后再拼接header的参数,最后的格式为:hKey1=hValue2&hKey2=hValue2&data=xxxx,而data的值就是上面请求参数拼接后的字符串,最后进行加密。
我们可以想象,如果客户端的拼接顺序和服务端拼接的顺序不一致,那么最后加密后的字符串肯定是不相等,那么最后必须是服务端返回一句话:签名不合法。
下面,我们看一下一开始SignUtil中对请求参数的拼接函数:
/**
* 将参数进行拼接
* 返回结果为: key1=value1&key2=value2&key3=value3
*
* @param map Map参数
* @return String
*/
public static String mapToString(Map<String, Object> map) {
StringBuffer builder = new StringBuffer();
map.forEach((key, value) -> builder.append(key).append("=").append(value).append("&"));
builder.deleteCharAt(builder.length() - 1);
return builder.toString();
}
我们可以看到,是遍历Map来进行拼接。Map如果以顺序分类,可以分为两大类:一种是有序(例如LinkedHashMap和TreeMap),一种是无序(HashMap)。如果支付服务提供的SignUtil提供的拼接方法如上,那么就是说,我们开发者并不知道支付服务端是传入哪种类型的Map来进行拼接;这时候如果支付服务端使用的是TreeMap,而我们自己在不知道的前提下使用了比较常用的HashMap,那么这时候就出大问题了,签名永远是错误的。
解决方法(支付服务方提供SignUtil):
1、第三方服务的开发文档必须提醒开发者该传入的Map类型。
拼接方法传入参数还是可以为Map,因为利用了泛型这样比较通用,但是呢文档该注重提醒开发者是传入有序的还是无序的实现类。
2、拼接方法传入参数指定Map类型,甚至具体到实现类。
虽然说这样没有使用到泛型,显得不够通用,可是正因为如此,能强制规定开发者的输入,能避免非常多不必要的坑。
/**
* 将参数进行拼接
* 返回结果为: key1=value1&key2=value2&key3=value3
*
* @param map HashMap参数
* @return String
*/
public static String mapToString(HashMap<String, Object> map) {
StringBuffer builder = new StringBuffer();
map.forEach((key, value) -> builder.append(key).append("=").append(value).append("&"));
builder.deleteCharAt(builder.length() - 1);
return builder.toString();
}
总结
如果是做提供第三方服务的程序猿,我们必须考虑到接口的通用性,还有更重要的是准确性;然后还有就是文档应该简洁而不简单,能让开发者看文档即可一次搞定对接,而不是浪费不必要的时间去尝试,去揣测第三方服务究竟是如何设计的。
对接第三方服务引起的小思考-回调和Sign算法的更多相关文章
- 如何更优雅地对接第三方API
本文所有示例完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/third 我们在日常开发过程 ...
- Java 后端开发常用的 10 种第三方服务
请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题. 严格意义上 ...
- 微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍
微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍 一.为什么会有 API Gateway 网关 随着微服务架构的流行,很多公司把原有的单 ...
- 对接第三方支付接口-获取http中的返回参数
这几天对接第三方支付接口,在回调通知里获取返回参数,有一家返回的json格式,请求参数可以从标准输入流中获取. //1.解析参数 , 读取请求内容 BufferedReader br; String ...
- 数据中心第三方服务、金融IT外包服务、社保医疗信息化解决方案,这三类业务是什么关系,区别在哪?
这个话题很大,牵扯很多,试着回答一下,算是胡扯了. 三类业务的关系,都是IT外包,至于外包的内容很杂.DC的外包,多半是基建和建维,一般不牵扯到软件开发,网站建设类的.金融IT外包就复杂多了,信息系统 ...
- 《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务
本文是对陶辉<深入理解Nginx>第5章内容的梳理以及实现,代码和注释基本出自此书. 一.upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例 (一)模块框 ...
- 如何从零开始对接第三方登录(Java版):QQ登录和微博登录
前言 个人网站最近增加了评论功能,为了方便用户不用注册就可以评论,对接了QQ和微博这2大常用软件的一键登录,总的来说其实都挺简单的,可能会有一点小坑,但不算多,完整记录下来方便后来人快速对接. 后台设 ...
- Heroku第三方服务接入指南(二)
上文我们讲了第三方服务.Heroku.用户三者的关系,这一篇进入正题,了解第三方厂商(下文简称厂商)怎样为Heroku开发服务.这里仅仅做简介,了解heroku大致是怎么做的.假设你的平台.希望接入第 ...
- kubernetes对接第三方认证
kubernetes对接第三方认证 kubernetes离线安装包地址 概述 本文介绍如何使用github账户去关联自己kubernetes账户.达到如下效果: 使用github用户email作为ku ...
随机推荐
- 牛客第五场多校 J plan 思维
链接:https://www.nowcoder.com/acm/contest/143/J来源:牛客网 There are n students going to travel. And hotel ...
- 基于C-W节约算法的车辆路径规划问题的Java实现
VRP问题概述 解决算法分类 项目描述 算法结果 车辆路线问题(VRP)最早是由Dantzig和Ramser于1959年首次提出,它是指一定数量的客户,各自有不同数量的货物需求,配送中心向客户提供货物 ...
- Linux 安装二进制MySQL 及 破解MySQL密码
1.确保系统中有依赖的libaio 软件,如果没有: yum -y install libaio 2.解压二进制MySQL软件包 tar xf mysql-5.7.24-linux-glibc2.12 ...
- SSM框架学习笔记(一)
Spring框架 Spring :是一个开源框架,起初是为解决企业应用开发的复杂性而创建的,但是现在已经不止 企业级应用开发,Spring的核心就是提供了一个轻量级的控制反转和面向切面编程. SPri ...
- TensorFlow读取数据的三种方法
tensortlfow数据读取有三种方式 placehold feed_dict:从内存中读取数据,占位符填充数据 queue队列:从硬盘读取数据 Dataset:同时支持内存和硬盘读取数据 plac ...
- 这可能是国内最全面的char RNN注释
char RNN代码来源于https://github.com/hzy46/Char-RNN-TensorFlow 前言 本人在学习char RNN的过程中,遇到了很多的问题,但是依然选择一行代码一行 ...
- kafka入门配置
问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行“随机读写”的原因是什么? 3.kafka集群consumer和producer状态信息是如何保存的? ...
- Redis压缩包win10快速启动之记录一
转载请标明出处: http://dujinyang.blog.csdn.net/ 本文出自:[奥特曼超人的博客] Redis压缩包 配置环境变量,直接CMD中启动,默认是打开redis.conf,当然 ...
- select2获取选中的val和text
用自己起的id就可以 获取val:$("#id").val() 获取text:$("#id").select2("data")[0].tex ...
- 浅谈HDFS(一)
产生背景及定义 HDFS:分布式文件系统,用于存储文件,主要特点在于其分布式,即有很多服务器联合起来实现其功能,集群中的服务器各有各的角色 随着数据量越来越大,一个操作系统存不下所有的数据,那么就分配 ...