这里使用的
Wechat Lib
版本是 2.0.6 ,高于此版本则不再能获得分享成功与否的回调。后来直接把基础库升级到 2.2.2 ,发现只是开发者工具拿不到回调而已,手机上还是还能拿到回调。
在使用微信小游戏的分享功能时,假如在 wx.showShareMenu
中设置了 withShareTicket: true
:
wx.showShareMenu({
withShareTicket: true // 分享到群可识别
});
那么,在分享成功回调中,假如分享到群则会得到一个 shareTickets
的字符串列表(列表长度与同时分享到的群数量相等,由于现在不能同时分享到多个群,所以这里列表长度是 1 ),假如分享到个人则无此项数据:
wx.shareAppMessage({
title: '分享标题',
imageUrl: imgUrl,
query: data.query ? data.query : '',
success: function (res) {
// 分享成功
console.log('---------- 分享成功')
if (res.shareTickets) {
}
},
fail: function (res) {
// 分享失败
console.log('---------- 分享失败')
}
})
然而, shareTickets 列表中的字符串并非是群的唯一标识,而是一个获取群加密数据的 key,即使是同一个群每次分享得到的字符串也是不同的,想要获得群的唯一标识 OpenGId ,需要如下操作:
-
通过
shareTicket
从 wx.getShareInfo 接口得到加密数据:encryptedData
和iv
; -
解密数据需要参考 加密数据解密算法 ,使用从
wx.login
获得的用户session_key
和上述获得的encryptedData
和iv
这三个数据,然后使用AES-128-CBC
算法(PKCS#7填充
)来解密数据。
关于 AES 算法
网上有大把的文章讲解的,这里就不做赘述了。
实现步骤
首先,参考微信官方给出的多种语言( C++ 、 Node 、PHP 和 Python )的 demo ,当然唯一有参考价值的就是 Node 版本的,其实只有两个脚本:
测试脚本
// demo.js
var WXBizDataCrypt = require('./WXBizDataCrypt')
var appId = 'wx4f4bc4dec97d474b'
var sessionKey = 'tiihtNczf5v6AKRyjwEUhQ=='
var encryptedData = 'CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS'+
'9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142d'+
'NCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6'+
'/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn'+
'/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4P'+
'C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns'+
'/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV'+
'oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuy'+
'Db/XcxxmK01EpqOyuxINew=='
var iv = 'r7BXXKkLb8qrSNn05n0qiA=='
var pc = new WXBizDataCrypt(appId, sessionKey)
var data = pc.decryptData(encryptedData , iv)
console.log('解密后 data: ', data)
工具脚本
// WXBizDataCrypt.js
var crypto = require('crypto')
function WXBizDataCrypt(appId, sessionKey) {
this.appId = appId
this.sessionKey = sessionKey
}
WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
// base64 decode
var sessionKey = new Buffer(this.sessionKey, 'base64')
encryptedData = new Buffer(encryptedData, 'base64')
iv = new Buffer(iv, 'base64')
try {
// 解密
var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
// 设置自动 padding 为 true,删除填充补位
decipher.setAutoPadding(true)
var decoded = decipher.update(encryptedData, 'binary', 'utf8')
decoded += decipher.final('utf8')
decoded = JSON.parse(decoded)
} catch (err) {
throw new Error('Illegal Buffer')
}
if (decoded.watermark.appid !== this.appId) {
throw new Error('Illegal Buffer')
}
return decoded
}
module.exports = WXBizDataCrypt
想要让 demo 运行起来也不难,直接初始化 node 工程结构:
$ cd Node
$ npm init # 入口脚本设置为 demo.js
$ npm i crypto
$ node . # 假如 npm init 时没有指定启动脚本为 demo.js , 则这里需要执行 node demo.js
结果发现,这里使用的 crypto
库是 Node 內建的库,假如要用于网页或微信小游戏上,需要进行改造构建自己的库。
自建库
由于微信小游戏中只需要使用到数据解码的接口,我们就只封装解码接口到库中,具体步骤如下:
-
创建库脚本
index.js
:var crypto = require('crypto') module.exports = function DecryptWXData(_encryptedData, _iv, _sessionKey) { // base64 decode var sessionKey = new Buffer(_sessionKey, 'base64') var encryptedData = new Buffer(_encryptedData, 'base64') var iv = new Buffer(_iv, 'base64') try { // 解密 var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv) // 设置自动 padding 为 true,删除填充补位 decipher.setAutoPadding(true) var decoded = decipher.update(encryptedData, 'binary', 'utf8') decoded += decipher.final('utf8') decoded = JSON.parse(decoded) } catch (err) { throw new Error('Illegal Buffer') } return decoded }
将
DecryptWXData
方法设置为模块的导出接口。 -
然后,安装
browserify
工具和gulify-js
工具:$ npm i -g browserify $ npm i -g uglify-js
browserify
是一个让浏览器可以加载使用 Node 模块的打包工具,本质上是会将改模块及其依赖的模块代码都打包到脚本中,而不再依赖外部代码,由于包含很多兼容不同浏览器的代码,因此打出来的库会比较到。uglify-js
是用来压缩 js 脚本生成.min.js
库脚本的工具。 -
生成库脚本:
$ browserify index.js -s DecryptWXData > decrypt.js $ uglify decrypt.js > decrypt.min.js
这里
-s DecryptWXData
是指定一个单例给外部访问(为了兼容微信小游戏),当然也不能直接使用,需要做一些小调整,将最终接口赋给window.DecryptWXData
。
发小最终压缩之后的库还要 413KB
,有点大,看来也只是作为一个临时方案,后面有时间再改用其他库。
引入库
将上面生成到库复制到工程的 libs/decrypt
目录下,创建对应的 .d.ts
提示脚本:
// decrypt.d.ts
declare function DecryptWXData(_encryptedData: string, _iv: string, _sessionKey: string);
然后在 egretProperties.json
中添加新库的配置:
{
"name": "decrypt",
"path": "./libs/decrypt"
}
直接当做全局方法来访问接口,使用如下:
let decryptedStr = DecryptWXData(encryptedData, iv, session_key);
其他
测试过程中可以借助在线工作验证输入输出结果是否正确:在线AES加解密 ,但是后来发现针对微信小游戏相关的数据进行解析,解密得到的都是空内容。
遇到的问题
我遇到了与 微信小游戏官方文档中的“加密数据解密算法”具体应该怎么实现 此贴一样的问题,即解密后的数据是一个 {"words":[],"sigBytes":0}
,最终发现原来使用库并不支持微信数据的解析。也可以使用 使用CryptoJS解决微信小程序用户信息解密 中的库,只是要自己将其打包到一个脚本中,而且需要自己做小游戏的适配:JS基础很烂的情况下找的微信小游戏解密方案
参考