🤖
AI审核中

小程序分享卡片:自己看正常,对方收到只有小程序名——后台的「分享签名校验」

文章摘要

在小程序里分享菜谱,发出方看到的卡片有标题有封面,接收方却只看到小程序名和一块灰色占位。排除了 onShareAppMessage、图片地址、临时文件和 promise、版本改动之后,发现原因是小程序后台开着「分享请求签名校验」:没带签名的分享会被平台降级成默认卡片。本文记录现象、排查过程、官方文档的要点,以及关闭校验和补齐签名两种解法。

做「炊想」小程序时遇到一个很迷惑的问题:在菜谱页点「分享」发给朋友,我自己这边看到的卡片好好的,有标题有封面;对方收到的却只有小程序名,封面是一块灰色占位。前后改了两版分享代码都没用,最后发现跟代码无关——是小程序后台开着一个「分享请求签名校验」。

这篇把现象、走过的弯路、真正的原因和两种解法记下来。

一、现象:同一张卡片,两边看到的不一样

发出方和接收方看到的分享卡片对比

具体是这样:

  • 发出方:卡片标题是代码里写的「「麻辣香锅」这样搭成一桌饭」,封面是这道菜的成品图,一切正常。
  • 接收方:标题变成了小程序名「炊想」,封面位置是灰底加一个小程序图标,像是图片没加载出来。
  • 不是偶发:昨天发的、今天发的都这样;换了分享代码、发了新版本以后还是这样。
  • 点开卡片能正常进入小程序、打开那道菜,只是卡片长得不对。

二、先排除了哪些可能

一开始的判断是「分享信息没传对」或者「图片对方加载不出来」,顺着这个思路查了一圈:

  1. onShareAppMessage 没写,或者数据还没回来就分享了? 发出方自己的卡片标题、封面都对,说明代码交出去的分享内容没问题。
  2. 封面图地址有问题(https、防盗链、过期)? 不带 Referer 直接请求图片地址,返回 200、image/jpeg、约 50KB,图片是好的。而且接收方连标题都丢了,图片问题解释不了标题。
  3. 分享时先把图下成本地临时文件、再用 promise 交给微信,对方拿不到? 2.0.11 改成同步返回、直接给 https 小图地址,接收方照旧,不是这里。
  4. 某一版改动引入的? 改动前发的卡片和改动后发的一样,不是版本问题。

排除到这里,有两条线索已经很明确了:

  1. 发出方正常、接收方异常:说明小程序把正确的数据交给了微信,问题出在微信「转交」给对方的环节。
  2. 标题和封面一起丢:对方看到的不是「图片挂了」,而是一张完整的默认卡片——默认标题就是小程序名,默认封面就是那个灰色占位。

也就是说,这张卡片被平台「降级」了。

三、真正的原因:分享安全校验

在微信官方文档里找到了《小程序分享签名》,说的正是这件事,要点是:

  • 小程序后台有个「分享安全校验」选项。开启后,每次自定义分享都要带上开发者签名,平台会校验,验签失败的分享会被降级。
  • 降级后的样子就是「默认分享标题 + 默认分享图」,和上面接收方看到的一模一样。
  • 开启后,自定义分享必须指定 imageUrl,否则直接展示降级卡片。
  • 签名对 appid、path、thumbDataHash(封面图的 SHA256)、title 四个字段计算,算法是 RSAwithSHA256(PSS 填充)或 SM2withSM3,私钥由开发者自己保管,公钥或证书要先配置到平台。

回到小程序后台一看,这个开关确实是开着的:

小程序后台「开发管理」里的安全管理配置:已开启对该应用的分享请求签名校验

位置在 小程序后台 → 管理 → 开发管理 → 开发设置 页面里的「安全管理配置」,提示文案是「已开启对该应用的分享请求签名校验」。我们的代码从来没有带过签名,所以每一次分享都被降级了。至于发出方为什么一直正常,我的理解是:发出方那张卡片是本机直接用分享数据画出来的,验签和降级只发生在下发给对方的环节——这也是这个坑最迷惑人的地方。

这个开关是什么时候打开的,已经无从考证了;它和开发者公钥、证书这些安全配置在一起,配置别的能力时顺手点开的可能性很大。

四、两种解法

解法一:不需要验签,就关掉它(我选的)

炊想的分享内容就是菜名和成品图,没有需要防篡改的敏感参数,所以直接点「关闭」:

关闭后的状态:开启后,微信开放平台将对该应用的分享请求进行签名校验

关闭后不用改代码、不用发版,重新分享一次,对方收到的卡片标题和封面就都正常了。线上已经发布的旧版本也一样恢复。

解法二:需要验签,就把签名补上

如果你的分享链接里带了业务参数(邀请码、活动 id 之类),担心被人改了再转发,就应该保留校验、把签名做完整。关键是私钥只能放在服务端,所以流程大致是:

  1. 在小程序后台配置开发者公钥(或证书),私钥放服务器环境变量。
  2. 服务端提供一个签名接口:入参是 path、title、imageUrl;服务端下载封面图算出 thumbDataHash(小写十六进制的 SHA256),按文档的规则拼签名串、用私钥签名,返回 thumbDataHash 和 signature。
  3. 小程序在分享之前就把签名拿好(比如菜谱详情加载完、封面图就绪时请求一次缓存起来)。onShareAppMessage 最好同步返回;用 promise 的话微信最多等 3 秒,超时就用默认值,等于又降级了。
  4. 分享对象里多带两个字段:
onShareAppMessage() {
  return {
    title: this.share.title,
    path: this.share.path,
    imageUrl: this.share.imageUrl,
    thumbDataHash: this.share.thumbDataHash, // 服务端算好的封面 SHA256
    signature: this.share.signature          // 服务端用私钥签的名
  }
}

签名串的拼法按文档来:四个字段按 key 的字典序排列,value 先 UTF-8 再标准 Base64(不带换行),用 & 连成 appid=...&path=...&thumbDataHash=...&title=...,再用 RSA-SHA256 + PSS(salt 长度 32)签名后 Base64。几个容易错的点:

  • path 要和分享对象里的一字不差,包括开头的 / 和 query 的编码方式。
  • 签的是 imageUrl 下载下来的图片字节的哈希。图片地址如果带缩放参数(比如七牛的 imageMogr2),要按带参数后实际返回的内容算。
  • 用包内图片(/assets/share-cover.jpg)当封面时,哈希按文档的说法应该就是这个文件本身的 SHA256,可以构建时算好(这一点我没有实测,做之前建议先用一张图验证)。
  • PSS 签名每次结果都不一样,这是正常的,只能用公钥验,不能拿两次结果比对。

五、遇到类似现象,怎么快速判断

  • 发出方正常、接收方的标题变成小程序名、封面是灰色占位:先别查代码,去后台看「安全管理配置」里的分享签名校验是不是开着。
  • 发出方自己看到的卡片就已经不对:那才是代码问题,查 onShareAppMessage 的返回值、异步数据、图片地址。
  • 只有封面不显示、标题是对的:多半是图片本身的问题(地址不可访问、格式不是 png/jpg、太大太慢)。

六、顺手修掉的两处

排查过程中改过的分享代码,有两处是真的值得改,最后保留了下来:

  1. 分享图被压扁了:封面用的是七牛缩略参数 thumbnail/500x400!,这里的 ! 表示「忽略原比例、强制缩放到这个尺寸」,1:1 的成品图被硬压成 5:4。改成先按比例铺满、再居中裁切:

    ?imageMogr2/thumbnail/!500x400r/gravity/Center/crop/500x400/format/jpg/quality/80
    
  2. 分享时不再临时下载图片:之前在分享那一刻先 downloadFile 成本地临时文件,再用 promise 交给微信,链路长、还受 3 秒上限约束。现在直接同步返回 500×400、约 50KB 的 https 小图地址,成品图还没出来时用包内的分享图兜底。

七、几点体会

  • 先分清「谁看到的」。这次如果一开始就注意到「发出方看到的是本地预览、接收方看到的才是平台下发的」,能少走很多弯路:本地正常而对方异常,问题多半在平台环节,而不是代码。
  • 「标题和图片一起丢」和「只丢图片」是两回事。前者是整张卡片被换成了默认值,后者才是资源加载问题。
  • 平台开关会改变行为,但不会出现在代码里。小程序后台的安全类开关(分享验签、API 安全、消息推送加密等)开了以后,最好在项目文档里记一笔:开了什么、为什么开、代码要配合做什么。
  • 文档里的关键词是「降级」。以后遇到「平台把我的内容换成了默认样式」这类现象,可以直接拿「降级」去搜。
0 条评论

如果你觉得文章对你有帮助,那就请作者喝杯咖啡吧

评论区 0

头像
 0 条评论