1
最近做 H5 有一个需求,要在网页链接分享到微信好友的显示描述和自定义缩略图,并在分享到朋友圈的时候显示缩略图.开始我觉得这是一个很简单的需求,应该在网页上配置一下就好了,后来才发现自己的真的是 too young,我花了一天半的时间来处理这个问题,最终得到了完美的解决,这里我记录下自己的踩坑经历,以便日后查阅.
2
微信官方是这么说的:
为规范自定义分享链接功能在网页上的使用,自 2017 年 4 月 25 日起,JSSDK“分享到朋友圈”及“发送给朋友”接口,自定义的分享链接,其域名或路径必须与当前页面对应的公众号 JS 安全域名一致,否则将调用失败。例如,当前页面是 http://www.abc.com/123,其公众号对应的JS安全域名为 www.abc.com 以及 www.xyz.com,则分享自定义链接 http://www.abc.com/456 可以成功,分享 http://www.xyz.com/123 或 http://www.def.com/123 均将失败。对于未接入微信 JSSDK 或已接入但 JSSDK 调用失败的网页,被用户分享时,分享卡片将统一使用默认缩略图和标题简介,不允许自定义。
意思是如果要自定义缩略图和简介,我们就需要接入微信的 jssdk.好,那我们现在就开始接入辣鸡微信的 jssdk.
微信文档地址:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#2
3.准备工作
如果要接入微信的 jssdk,必要条件如下,如果缺少其中任意一个,就不用继续看下去了,因为不能接入 jssdk:
- 一个认证了的微信公众号(需要公众号的 appid 和 screct)
- 一个已经备案的域名
可选的配置:
- 后台开发语言(这里我选用了 nodejs).
- js 安全域名(要求 80/443 端口),这个后面还会提到.
- ssl 证书(用于添加 https,这里推荐用 https,防劫持这样的有点就不说了,还有一个很重要的原因就是:如果不是 https 的话,使用微信浏览页面会有一个 confirm 安全的页面,确认以后虽然可以进入 h5,但是在 ios 下底部会有一个难看的导航栏,十分影响体验)
4.微信公众号配置
(1)ip 白名单配置
ip 白名单主要是用于后端根据 appid 和 screct 获取 access_token,只有在白名单中的 ip 才可以访问微信的官方接口.
(2)添加 js 安全域名
需要注意的是:
- 安全域名最多只能填写三个,而且一个月内最多可以修改并保存三次.
- 和上面提到的一样,这个域名必须是备案过的;
- 需要下载微信提供的 txt 验证文件上传到 web 服务器网页的路径下,例如:我使用 vue(webpack)打包的文件在 https://example.com/nh 下,那么这个文件也存放在这个路径下,确保微信可以虎获取到这个文件就可以,因为如果获取不到是无法添加域名的.(这一步其实是为了限制一个认证公众号可以绑定的域名数量)
5 后端服务开发(使用 nodejs)
使用 koa 框架:
1 | var http = require("http"); |
需要注意的事项:
- 1.微信获取 access_token 和 ticket 的接口是有访问频率限制的,据称是每天 2000 次.
生成签名之前必须先了解一下 jsapi_ticket,jsapi_ticket 是公众号用于调用微信 JS 接口的临时票据。正常情况下,jsapi_ticket 的有效期为 7200 秒,通过 access_token 来获取。由于获取 jsapi_ticket 的 api 调用次数非常有限,频繁刷新 jsapi_ticket 会导致 api 调用受限,影响自身业务,开发者必须在自己的服务全局缓存 jsapi_ticket 。参考以下文档获取 access_token(有效期 7200 秒,开发者必须在自己的服务全局缓存 access_token):https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html用第一步拿到的access_token 采用 http GET 方式请求获得 jsapi_ticket(有效期 7200 秒,开发者必须在自己的服务全局缓存 jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
- 2.签名算法
签名生成规则如下:参与签名的字段包括 noncestr(随机字符串), 有效的 jsapi_ticket, timestamp(时间戳), url(当前网页的 URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式(即 key1=value1&key2=value2…)拼接成字符串 string1。这里需要注意的是所有参数名均为小写字符。对 string1 作 sha1 加密,字段名和字段值都采用原始值,不进行 URL 转义。即 signature=sha1(string1)。
这里我直接用了微信给的 example 中的代码:
1 | //sign.js |
可以在这里验证生成微信签名的正确性:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
6.前端开发
(1) 引入 JS 文件
在需要调用 JS 接口的页面引入如下 JS 文件,(支持 https):http://res.wx.qq.com/open/js/jweixin-1.6.0.js
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.6.0.js (支持 https)。
(2)通过 config 接口注入权限验证配置
所有需要使用 JS-SDK 的页面必须先注入配置信息,否则将无法调用(同一个 url 仅需调用一次,对于变化 url 的 SPA 的 web app 可在每次 url 变化时进行调用,目前 Android 微信客户端不支持 pushState 的 H5 新特性,所以使用 pushState 来实现 web app 的页面会导致签名失败,此问题会在 Android6.2 中修复)。
1 | wx.config({ |
(3)通过 ready 接口处理成功验证
1 | wx.ready(function() { |
(4)通过 error 接口处理失败验证
wx.error(function(res){
// config 信息验证失败会执行 error 函数,如签名过期导致验证失败,具体错误信息可以打开 config 的 debug 模式查看,也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名。
});
样例:
1 | //index.html |
7 问题汇总
运维
https
注意接口一定要添加 https,而且绑定域名.
如果不绑定域名,使用 https://ip这样的形式,使用微信开发者工具调试时接口不会报错,但是在手机端会拦截请求.
证书可以到阿里云上申请,域名可以到 freedom 申请免费域名,然后在 dnspod 添加域名解析以及证书与域名的绑定关系;
部署服务
我使用了 docker 部署了服务,这样可以免去配置环境等问题.
1 | # Dockerfile |
暴露端口
部署在阿里云上的主机记得开放端口方便外部访问:
前端开发
invalid signature
1.url
绝大多数invalid signature
问题的原因都是 url 的问题,
url 必须与之前填入的 js 安全域名相同.
确保 url 是动态生成的,同时一定要使用 encodeUrlComponent
编码!!!
1 | encodeURIComponent(window.location.href.split("#")[0]); |
2.确保签名算法无误
签名生成规则如下:
参与签名的字段包括有效的 jsapi_ticket(获取方式详见微信 JSSDK 文档), noncestr (随机字符串,由开发者随机生成),timestamp (由开发者生成的当前时间戳), url(当前网页的URL,不包含#及其后面部分。注意:对于没有只有域名没有 path 的 URL ,浏览器会自动加上 / 作为 path,如打开 http://qq.com 则获取到的 URL 为 http://qq.com/)。
对所有待签名参数按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串 string1。这里需要注意的是所有参数名均为小写字符。
接下来对 string1 作 sha1 加密,字段名和字段值都采用原始值,不进行 URL 转义。即 signature=sha1(string1)。
后端开发
api 调用限制
微信对 access_token 以及 ticket 的获取有每日调用次数的限制,对于稍大的项目就需要考虑缓存 ticket(ticket 的有效期是 7200s),这一点微信文档中有详细的说明.
安全相关
appid 和 screct 不能轻易泄露,返回给前端的数据中需要传 appid.