背景

​ 最近在对接一个同事写的支付公用模块,然后对第三方服务引起一两个小思考。

思考

回调

来看看我们同事是如何做回调的。

首先,请求支付接口的时候,将回调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算法的更多相关文章

  1. 如何更优雅地对接第三方API

    本文所有示例完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/third 我们在日常开发过程 ...

  2. Java 后端开发常用的 10 种第三方服务

    请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题. 严格意义上 ...

  3. 微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍

    微服务架构学习与思考(10):微服务网关和开源 API 网关01-以 Nginx 为基础的 API 网关详细介绍 一.为什么会有 API Gateway 网关 随着微服务架构的流行,很多公司把原有的单 ...

  4. 对接第三方支付接口-获取http中的返回参数

    这几天对接第三方支付接口,在回调通知里获取返回参数,有一家返回的json格式,请求参数可以从标准输入流中获取. //1.解析参数 , 读取请求内容 BufferedReader br; String ...

  5. 数据中心第三方服务、金融IT外包服务、社保医疗信息化解决方案,这三类业务是什么关系,区别在哪?

    这个话题很大,牵扯很多,试着回答一下,算是胡扯了. 三类业务的关系,都是IT外包,至于外包的内容很杂.DC的外包,多半是基建和建维,一般不牵扯到软件开发,网站建设类的.金融IT外包就复杂多了,信息系统 ...

  6. 《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务

    本文是对陶辉<深入理解Nginx>第5章内容的梳理以及实现,代码和注释基本出自此书. 一.upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例 (一)模块框 ...

  7. 如何从零开始对接第三方登录(Java版):QQ登录和微博登录

    前言 个人网站最近增加了评论功能,为了方便用户不用注册就可以评论,对接了QQ和微博这2大常用软件的一键登录,总的来说其实都挺简单的,可能会有一点小坑,但不算多,完整记录下来方便后来人快速对接. 后台设 ...

  8. Heroku第三方服务接入指南(二)

    上文我们讲了第三方服务.Heroku.用户三者的关系,这一篇进入正题,了解第三方厂商(下文简称厂商)怎样为Heroku开发服务.这里仅仅做简介,了解heroku大致是怎么做的.假设你的平台.希望接入第 ...

  9. kubernetes对接第三方认证

    kubernetes对接第三方认证 kubernetes离线安装包地址 概述 本文介绍如何使用github账户去关联自己kubernetes账户.达到如下效果: 使用github用户email作为ku ...

随机推荐

  1. super(classname,self).__init__() 作用

  2. 网页去重之Simhash算法

    Simhash算法是Google应用在网页去重中的一个常用算法,在开始讲解Simhash之前,先了解——什么是网页去重?为什么要进行网页去重?如何进行网页去重,其基本框架是什么?   网页去重,顾名思 ...

  3. 在nginx日志access log可以记录POST请求的参数值

    1)      在nginx日志access log可以记录POST请求的参数值 实现程度:日志中可以显示POST请求所提交的参数值 问题: 日志中文显示十六进制(在配置文件中配置中文也无效) 没有对 ...

  4. web性能优化实践

    一.SQL查询优化 1.循环中有多次查询sql,改为在循环外一次查询后再处理 2.循环多次插入,改为组装好数据后批量插入 3.梳理业务逻辑能一次查完的,绝不分多次查 4.索引用起来 5.分页查询 二. ...

  5. 即时聊天APP(四) - 联系人和会话

    联系人和会话界面使用的是RecyclerView进行滑动显示,并将好友列表存储至数据库,以供下次登录时使用,RecyclerView在后面我会详细介绍,这里略过. 联系人初始化时读取数据库并展示: / ...

  6. CentOS升级内核方法

    查询现在系统的kernel安装包:rpm -qa |grep kernel 删除不用的内核安装包:rpm -e xxx centos 6升级:https://blog.csdn.net/wh21121 ...

  7. 如何使用rsync备份

    已知3台服务器主机名分别为web01.backup .nfs主机信息见下表: 角色 外网IP(NAT) 内网IP(LAN) 主机名 WEB eth0:10.0.0.7 eth1:172.16.1.7 ...

  8. 众咖云集的 PyCon 2019 上海站,大佬们都讲了啥

    9 月 21 号周六,我参加了 PyCon China 2019 上海站,这是每年一届的 Python 中国开发者大会. 今年的上海站比往年的阵容扩大很多,「流畅的 Python」作者.Flask 作 ...

  9. Hadoop入门 之 Hadoop常识

    1.Hadoop是什么? 答:Hadoop是开源的分布式存储和分布式计算平台. 2.Hadoop的组成是什么? 答:Hadoop由HDFS和MapReduce这两个核心部分组成. HDFS(Hadoo ...

  10. vim编辑python脚本时Tab补全

    所属分类:成长之路 使用Linux写python脚本的时候,初期最痛苦的是什么?当然是各种库的不熟悉,知道了库,里面的方法还要挨个看,挨个记. 所以这时候,很多小伙伴使用了ipython,最强大的功能 ...