背景

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

思考

回调

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

首先,请求支付接口的时候,将回调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. CF994B Knights of a Polygonal Table 第一道 贪心 set/multiset的用法

    Knights of a Polygonal Table time limit per test 1 second memory limit per test 256 megabytes input ...

  2. cesium页面小控件的隐藏

    cesium页面小控件的隐藏 1   创建一个Viewer var viewer = new Cesium.Viewer('cesiumContainer');//cesiumContainer为di ...

  3. 良许 | 听说,有个同事因为关闭服务器被打进 ICU ……

    提问:你是如何关闭电脑的? 普通青年 文艺青年 二逼青年 你是属于哪一种呢? 实话说, 这三种良许都干过~ 还好我没有对服务器这么做, 否则-- 分分钟被打进 ICU -- 1. 关机命令知多少 对于 ...

  4. Vue使用MathJax动态识别数学公式

    本人菜鸟一名,如有错误,还请见谅. 1.前言 最近公司的一个项目需求是在前端显示Latex转化的数学公式,经过不断的百度和测试已基本实现.现在此做一个记录. 2.MathJax介绍 MathJax是一 ...

  5. c++调试在容器释放内存时报Unknown Signal 或 Trace/breakpoint trap异常

    在做一道题时,用到的板子中出现了很多的容器的使用,,一开始都是开MAXN大小的容器,,但是有几率出现程序运行完后不正常退出,, 在多次尝试断点调试后,发现主要的异常是程序在结束时,要进行资源的释放,, ...

  6. Spring Boot2 系列教程(四)理解Spring Boot 配置文件 application.properties

    在 Spring Boot 中,配置文件有两种不同的格式,一个是 properties ,另一个是 yaml . 虽然 properties 文件比较常见,但是相对于 properties 而言,ya ...

  7. 8 分钟入门 K8s | 详解容器基本概念

    作者| 阿里巴巴高级开发工程师 傅伟 一.容器与镜像 什么是容器? 在介绍容器的具体概念之前,先简单回顾一下操作系统是如何管理进程的. 首先,当我们登录到操作系统之后,可以通过 ps 等操作看到各式各 ...

  8. Go语言获取系统性能数据gopsutil库

    psutil是一个跨平台进程和系统监控的Python库,而gopsutil是其Go语言版本的实现.本文介绍了它的基本使用. Go语言部署简单.性能好的特点非常适合做一些诸如采集系统信息和监控的服务,本 ...

  9. Dockfile 生成docker镜像文件大小的比较

    下面就是我针对docker file同一个文件,按照layer层的个数的多少,分别构建了两个镜像的jenkins-master.两者大小相差300MB. <1> layer层数太多,没有将 ...

  10. html5表单与Jquery Ajax配合使用

    html5的表单控件提供了很多格式检测功能,可以省去很多烦人的javascript验证代码,例如pattern属性和require属性,但触发的条件是表单提交,如果想通过ajax提交表单,就出现了不能 ...