纷享销客事件变更订阅接口到底怎么用?
纷享事件变更订阅是一种数据事件主调的服务能力,用户可以通过在纷享侧订阅事件的方式,来监听数据的变化,从而能够以一种很轻量级的方式就能达到几乎近实时的数据同步的效果。
在纷享销客开放平台推出这个接口(2020年初,当时这个接口叫事件回调接口)之前,要想实现纷享销客和其它系统集成,需要通过工作流执行纷享销客的自定义函数,利用Fx.http接口把数据POST到第三方服务器,进行业务逻辑处理。现在依然有很多集成是通过这种方式进行的,简单快捷,唯一存在的问题是可能存在安全隐患,有基础编程能力的人,看到函数源码,都会暴露接口和参数,虽然有一些其它的安全措施,但是还是存在安全风险。而事件变更订阅接口是通过AES对称加密进行数据传输的,安全性更高,所以还是建议通过纷享的事件变更订阅接口来完成与其它系统集成。
一、在纷享里设置事件回调
第一个大坑就在配置这个过程。配置在操作路径在:后台->系统对接管理->事件回调设置。需要填写两项内容:
- 事件回调URL,URL规则:协议为http或https,支持域名或者IP访问
- 解析密钥,密钥规则:44位长度的Base64编码字符串,前43位为A-Z、a-z、0-9、+、/中的任意字符,最后一位固定为后缀=
注意:第一个大坑就在这里,解析密钥,官方会直接生成一个,但是官方生成的密钥根本解码不出来!不出来!!不出来!!!也就是说,官方是真的生成一个44随机字符串连接 "=",想用Base64解码出来明文密钥,根本解码不出来。这里还是需要填写自己对32位的密钥base64编码后的密钥。
如果不知道自己编码的密钥对不对,可以通过现线网站进行比对,如果你的代码和在线网站编码或者加密结果一样,那就应该是没问题的。以下两个网站,可以参考使用。
基本上所有编程语言里都有用于base64编码的函数,比如对32个 a("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") 进行base64编码的结果就是:YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE=
直接把密钥base64编码后的结果填写到 解析密钥 文本框里就可以了。
接下来是URL的配置,这里需要注意的是,必须按官方要求接口能正确返回JOSN,才能配置成功,不然一致会提示URL格式错误,很多小伙伴都是停留在了这一步,因为配置不成功进入不了下一步操作。那到底返回该返回什么结果呢?
{
"signature":"7c6c4c03b753911a4a8541f48f3726af3be82962",
"timestamp":1629859136,
"nonce":5,
"encryptedResult":"mrdH5+Fn\/o2ImucpQfk0VQ=="
}
- signature 就是把时间戳、随机数、success加密后的结果、密钥(base64编码后的结果)按照顺序拼接成一个字符串,然后SHA1加密;
- timestamp 时间戳;
- nonce 随机数;
- encryptedResult 使用密钥对 "success" 进行加密,使用ASEKey做AES/CBC/PKCS5Padding加密;
注意:第二个容易出错的地方就在这里!因为不同语言的加密结果可能不一样!
Java
//官方提供的Java示例
String result = "success";
String aesKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C=";
byte[] plaintextBytes = result.getBytes();
byte[] aesKeyBytes = Base64.decodeBase64(aesKey);
SecretKeySpec keySpec = new SecretKeySpec(aesKeyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKeyBytes, 0, 16);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
String encryptedResult = Base64.encodeBase64String(encryptedBytes); //转换为64位编码
PHP
//php示例
//接受事件推送
public function receive(){
$key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
$encryptedResult = $this->encrypt("success", $key);
$time = time();
$result = array(
"signature" => sha1($time."5".$encryptedResult.base64_encode($key)),
"timestamp" => $time,
"nonce" => 5,//偷懒
"encryptedResult"=> $encryptedResult
return json($result);
//AES加密
public function encrypt($data, $key)
$data = openssl_encrypt($data, 'AES-256-CBC', $key, 0, substr($key, 0, 16));
return $data;
//AES解密
public function decrypt($data, $key)
return openssl_decrypt($data, 'AES-256-CBC', $key, 0, substr($key, 0, 16));
}
官方指定的加密方法是:AES/CBC/PKCS5Padding,使用32个 a 对 success 加密结果是:mrdH5+Fn/o2ImucpQfk0VQ==
而PHP加密出来的结果是:7ftjnOVFmYLge0wm396m5w==
这个问题我也思考了好久,找了很多资料,最后发现是:mcrypt_decrypt在PHP7.*已经被弃用,取而代之的是openssl_decrypt/encrypt,而openssl_decrypt/encrypt AES-128-CBC 填充算法,只支持16位密钥,要支持32密钥需要使用 AES-256-CBC,加密网站128位加密的结果却和Java加密结果一样,这样就很容易迷惑。大神勿喷,没有深入研究过AES算法。
到这里如果都没有问题,基本上就配置没问题了,然后就是勾选要订阅哪些对象的哪些事件,支持预置对象和自定义对象,主要有:对象创建、对象更新、对象作废、对象恢复、对象删除。
二、解密接收到的加密数据
纷享事件订阅POST过来的数据是一个加密的字符串,需要解密才能获取到明文数据,如果理解了加密,那解密就很简单了。
//官方Java解密示例
String encryptedContent = "6Hrlh1CuiFg0R1mVHwAZMWDwx0m55Hj6M1PakQQHcoxP2ZzVKHWkeen5NeBM2UwlGQT11ZJYkRNg2k+Z3kdTCoopUsYINrY1y+dIlGGpCNbnEKynmqWppRnFTgYSdseR";
String aseKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C=";
byte[] ciphertextBytes = Base64.decodeBase64(encryptedContent); // decode加密密文结果
byte[] aesKeyBytes = Base64.decodeBase64(aesKey); // decode 秘钥
SecretKeySpec keySpec = new SecretKeySpec(aesKeyBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(aesKeyBytes, 0, 16); // 初始化向量
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 加密模式为CBC
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);
String result = String(plaintextBytes, Charsets.UTF_8); // 解密结果
解密后结果:
{
"tenantId":682717,