抽奖助手小程序研究
一个只允许内部抽奖的请求如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| POST /v2/lottery/8zKwAa7L1Ff/join HTTP/2 Host: lucky.nocode.com Content-Length: 483 Version: 2.6.17 Client-Version: 3.9.10 Sdkversion: 3.4.3 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo0OTk4ODc1NiwiaWF0IjoxNzE2MTcxODkzLCJleHAiOjE3MTY3NzY2OTN9.Bnc6Kge43eqbUiPrJ4JB3Fbou_W60-cgyk_6lZhT-io Content-Type: application/json User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) XWEB/9129 Timestamp: 1716256693801 Xweb_xhr: 1 Platform: wechat Sign: f9736e8cbbf45eab3bd3bf4a27a5f36ebdf19a9a X-Request-Id: 95476190-1715-11ef-b771-3bf552015d6c Accept: */* Sec-Fetch-Site: cross-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://servicewechat.com/wx01bb1ef166cd3f4e/2121/page-frame.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9
{"form_id":"","source":{"type":"groupchat","data":"{\"encryptedData\":\"NEgbNTbr0CaM+KLYz33+r684aJQBIz9KPFMsMysFeKt8k+M4RNX8Y3Q69LFbUwVNV2WdyiowzIsgytwFJKSUM8se9LaX/JkdU5ch5hzf8evszJq7FnpkvBZteWLy0H03Nye1/xs5VFOyeEaInFcrBw==\",\"iv\":\"LVE6y+fy50i0R3tPEiuffw==\"}"},"direct_share_info":{"encryptedData":"Pgr8C/iW0s2TJTSRY8y/E6KJsQZzGm6n3539Uk4fK52AlDS/z5K8gRxjBU52Wdt8fir6tMLYgaGlgzjOiB+/aNjZuH/QvYPWM1AZD3TH+NLLWhT7CgavNBUwOXe+L53/","iv":"ljRdbV7izYorDqml1CgAGQ=="},"location":null}
|
数据开起来加密了,实际上可以忽略,因为POST中的数据是小程序API返回的。
source的数据来自wx.getShareInfo()
direct_share_info的数据来自wx.authPrivateMessage()
实例数据:
| {"errMsg":"getShareInfo:ok","errno":0,"errorCode":0,"encryptedData":"0/flEF4dUxS/FtUHLthO/j+SJFgFjrc7xpYV0oM2LL1k5517/6GYFW2NBjLz20Pai4ahc/7a8BQ3qStz8bl6Ektc3C8Do/Bk6ANcRJzdT0Jg9e0lDr4UFBVfpDTYvWxokJXaDuwPndOCh0RUhNCCgg==","iv":"d17cSep9K4HqPlMBCuz2Ng==","cloudID":"80_FzgitqrYtDns7ciKxjhnZ4kGaZSf3j0a_Qh_ih5APlSvou8kf3h_DJCSKNU"}
{"iv":"fK+HbsG4G80QEsfuDMtZMg==","encryptedData":"xHUp9e2SDGoVI1NfcEyM0qHT+GrW+hjT/iV5U3tc4yHGfwGBB64ALG2KMHNEDxEFQumFnVsG6H9/7ReOzOnRgSAzN3mpREUZVRBhbTyv5vU=","valid":true,"errMsg":"authPrivateMessage:ok"}
|
getShareInfo的数据可以随意填充,服务器不会进行校验。
当开启“群成员参与”时,服务器会严格校验authPrivateMessage返回的内容。

对于限制了参与人数、且仅允许群内成员参与的抽奖活动,可以提前进入群内之前的抽奖小程序链接,调用wx.authPrivateMessage和 wx.getShareInfo获取必要参数。
调用这两个接口都需要shareTicket。 至于shareTicket如何生成,我并没有找到答案。我猜测是随机生成,然后通过一些微信接口将生成的值与当前微信账号绑定,因为在断网的情况下,依然可以获取到shareTicket。

| wx.authPrivateMessage({shareTicket: '38538646-26e4-4e9e-a936-3481196a35ab',success: function(e) {console.log(JSON.stringify(e))}})
wx.getShareInfo({shareTicket: '38538646-26e4-4e9e-a936-3481196a35ab',timeout: 2e3,success: function(e) {console.log(JSON.stringify(e))}})
|

签名
签名流程:
将POST请求中的json转成query格式的字符串(key升序)
计算上一步字字符串的md5
拼接字符串: 请求方法url 、上一步的md5、时间戳、密钥
| post:/lottery/8zLwZp27ojC/join::d33f07a0efe04c68feb2d4aee04a2ea9:1716279736660:nuTW7z+c(H?MD+kbWR6XGnb6Be2v8ttU:1
|
计算sha1
json转query字符串的过程中存在BUG,比如前面的那个请求,转成的字符串为:
| direct_share_info=object&form_id=&location=&source=object
|
direct_share_info和source的值被强制转成了字符串”object”,导致修改这两个参数后签名不会变。
参考资料
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share/private-message.html
https://zhuanlan.zhihu.com/p/36045620