• 前言:

业务来源:自主研发的手机app软件有分享文章到微信或者QQ以及微博的功能,而在微信中再次点击分享按钮的时候,情况就出现的不可把控了:

文章显示的缩略图不能正常显示;文章的简介不能显示……而我们领导的要求便是再次分享的时候,显示自己app的logo,于是就开始了微信jsdk的整天研究。(题外话:其实在去年,自己就看过微信jsdk文档,但是苦于研究不出什么名堂,而且当时是找了一种‘投机取巧’的方法,算是完美的解决了当时的需求,但是意外总是在不经意间就降临了,那天突然看见了微信公众号中说道:“

JSSDK自定义分享接口的策略调整

2017-03-29 微信团队 微信开发者

为规范自定义分享链接功能在网页上的使用,自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说明文档》,请开发者及时完成调整。”)

这样的突发情况就如晴天霹雳,让自己不得不面对再次拾起关于微信jsdk的研究。历经“山重水复”的过程,特地把踩过的坑粉分享出来,这样小伙伴们就不会陷入同样的错误中了(下面的版本是基于nodejs环境下的~)。

  • 过程:

这是官方文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN,先看文档再上手;按照步骤,一步步进行;关于具体的文档中都有,小伙伴们仔细看文档肯定就能理解,接下来就只是说说自己在实现这个功能时,问题出在哪里吧……

1.注意点一:(首先配置js接口安全域名,即程序运行的网址是什么,需要下载的文件,官网上直接下载配置即可;配置完成后,可在“开发者中心”查看对应的接口权限);

2.在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

  1. <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

3.注意点二,也是最关键最核心的部分,因为如果这里配置成功,后面便可成功的调用微信粉分享接口,否则便失败;

wx.config({

debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

appId: '', // 必填,公众号的唯一标识

timestamp: , // 必填,生成签名的时间戳

nonceStr: '', // 必填,生成签名的随机串

signature: '',// 必填,签名,见附录1

jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

});

这便是需要配置的东西,下面谈谈心酸的踩坑经历:

1)timestamp        //当前时间戳  这里是秒级别的时间戳格式

  1. let timestamp = parseInt( new Date().getTime()/1000);//秒级别

2)nonceStr        //随机字符串  这里是保留长度为16的随机字符串

  1. let nonceStr = Math.random().toString(36).substr(2,16);//长度为16的随机字符串

3)signature   ——>  重点来了!关于这个签名的取值,硬生生的耗费了自己很长很长时间,也是因为自己不够聪明,没看清楚文档写的,这里还是要感谢老大,昨晚陪自己加班快十点找问题,直到今早上终于完美解决掉。
           关于动态产生的签名:

a.先使用appid,secret,通过get请求“https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=appid&secret=secret”,得到access_token;

b.用得到的access_token,再次通过get请求”https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+access_token+'&type=jsapi”,最终得到jsapi_ticket;

c.签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。例如:

jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value

这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义,得到signature。

即signature=sha1(string1)。

注意点:重点!

生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。原本打算在项目中使用session来缓存这两个,最终在老大的知道下,这里使用的是redis存储缓存;

完整前台HTML页面代码:

  1. <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
  2. <script>
  3. jQuery(function () {
  4. //页面初始化完成后,先post请求将当前页面的url传到后台,进行signature的计算,而这里一定记得要: encodeURIComponent()一下,去后台后在: decodeURIComponent(),要不就是频繁报“invalid signature"的错误
  5. jQuery.post("/share/getshare", {"url": encodeURIComponent(window.location.href.split('#')[0]),"t": new Date().getTime()}, function (result) {
  6. if (result.errno != 0) {
  7. alert("您当前的网络不稳定请稍后再试!");
  8. return;
  9. }
  10. var shareUrl = result.data.url;
  11. wx.config({
  12. debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  13. appId: 'wxf919e3b61eca36ae', // 必填,公众号的唯一标识
  14. timestamp: result.data.timestamp, // 必填,生成签名的时间戳
  15. nonceStr: result.data.nonceStr, // 必填,生成签名的随机串
  16. signature: result.data.signature,// 必填,签名,见附录1
  17. jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ','onMenuShareWeibo'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
  18. });
  19. wx.ready(function () {
  20. var title = "{{data.title}}";
  21. var desc = "{{data.scontent}}"?"{{data.scontent}}":"";
  22. var imgUrl = "http://static.gangguwang.com/image/2016/12/16/16/55/5853abfc101887000800111f.jpg";
  23. wx.onMenuShareTimeline({//“分享到朋友圈”
  24. title: title, // 分享标题
  25. link: shareUrl, // 分享链接
  26. imgUrl: imgUrl, // 分享图标
  27. success: function () {
  28. // 用户确认分享后执行的回调函数
  29. },
  30. cancel: function () {
  31. // 用户取消分享后执行的回调函数
  32. }
  33. });
  34. wx.onMenuShareAppMessage({//“分享给朋友”
  35. title: title, // 分享标题
  36. desc: desc, // 分享描述
  37. link: shareUrl, // 分享链接
  38. imgUrl: imgUrl, // 分享图标
  39. type: 'link', // 分享类型,music、video或link,不填默认为link
  40. dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
  41. success: function () {
  42. console.log('succ~');
  43. // 用户确认分享后执行的回调函数
  44. },
  45. cancel: function () {
  46. console.log('fail~');
  47. // 用户取消分享后执行的回调函数
  48. }
  49. });
  50. wx.onMenuShareQQ({//“分享到QQ”
  51. title: title, // 分享标题
  52. desc: desc, // 分享描述
  53. link: shareUrl, // 分享链接
  54. imgUrl: imgUrl, // 分享图标
  55. success: function () {
  56. // 用户确认分享后执行的回调函数
  57. },
  58. cancel: function () {
  59. // 用户取消分享后执行的回调函数
  60. }
  61. });
  62. wx.onMenuShareWeibo({//“分享到腾讯微博”
  63. title: title, // 分享标题
  64. desc: desc, // 分享描述
  65. link: shareUrl, // 分享链接
  66. imgUrl: imgUrl, // 分享图标
  67. success: function () {
  68. // 用户确认分享后执行的回调函数
  69. },
  70. cancel: function () {
  71. // 用户取消分享后执行的回调函数
  72. }
  73. });
  74. });
  75. });
  76. })
  77. </script>
  1. 后台的处理逻辑:
  1. //这里先要引入sha1依赖包
  2. //getshare方法便是获得动态签名的方法(这是基于node.js环境下的)
  3. async getshareAction(){
  4. //将前台得到的url先decodeURIComponent()一下再使用
  5. let url = decodeURIComponent(this.post().url);
  6. //当前时间戳
  7. let timestamp = parseInt( new Date().getTime()/1000);
  8. //随机字符串
  9. let nonceStr = Math.random().toString(36).substr(2,16);
  10. ////获取缓存信息 存在redis中,从 redis 里获取缓存(全局缓存jsapi_ticket -----> 有效期7200秒)
  11. let ticket = await think.cache('ticket_weixinshare_ywg', undefined, {type: 'redis'});
  12. let access_token = await think.cache('token_weixinshare_ywg', undefined, {type: 'redis'});
  13. let req = think.promisify(request.get);
  14. if(!access_token){//不存在
  15. let options1 = {//获取 acess_token
  16. url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=appid&secret=secret',
  17. method: "get"
  18. };
  19. let res = await req(options1);
  20. access_token = JSON.parse(res.body).access_token;
  21. //设置缓存 access_token(这里是thinkjs的redis设置缓存的方法)
  22. await think.cache('token_weixinshare_ywg', access_token);
  23. }
  24. if(!ticket){
  25. let options2={//计算 jsapi_ticket
  26. url: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+access_token+'&type=jsapi',
  27. method: "get"
  28. };
  29. let res2 = await req(options2);
  30. if(JSON.parse(res2.body).errcode==0){
  31. ticket=JSON.parse(res2.body).ticket;
  32. //设置缓存 ticket
  33. await think.cache('ticket_weixinshare_ywg', ticket);
  34. }else{
  35. console.log('=========error==='+JSON.parse(res2.body).errmsg)
  36. }
  37. }
  38. //按照字段名的ASCII 码从小到大排序(字典序)
  39. let raw = async (args) =>{
  40. let keys = Object.keys(args);
  41. keys = keys.sort();
  42. let newArgs = {};
  43. keys.forEach(function (key) {
  44. newArgs[key.toLowerCase()] = args[key];
  45. });
  46. let string = '';
  47. for (let k in newArgs) {
  48. string += '&' + k + '=' + newArgs[k];
  49. }
  50. string = string.substr(1);
  51. return string;
  52. };
  53. let ret = {
  54. jsapi_ticket: ticket,
  55. nonceStr: nonceStr,
  56. timestamp: timestamp,
  57. url: url
  58. };
  59. let string1 = await raw(ret);
  60. //将得到的字符串进行sha1加密,然后返回将 wx.config()中所需要的值返回到前台页面
  61. let signature = sha1(string1);
  62. this.success({"timestamp":timestamp,"nonceStr":nonceStr,"signature":signature,"url":url});
  63. }

至此运行,便可以神清气爽的看见下面的提示:

而昨天一整天的情况便是:

而对于“invalid signature”的问题排查官方文档也给出了相应的介绍:

2.invalid signature签名错误。建议按如下顺序检查:

1.确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。

2.确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。

3.确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的GET参数部分,但不包括'#'hash后面的部分。

4.确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

5.确保一定缓存access_token和jsapi_ticket。

6.确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去'#'hash部分的链接(可用location.href.split('#')[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

而自己按照步骤来实现的获取签名,也总是报错的根本原因就是url取得有问题,因此建议大家自己阅读文档,获取到页面的完整路径并先进行处理后再使用。(自己使用的页面完整url路径:http://127.0.0.1:8361/share?id=xxx).

4.成功获得签名后,便可以通过ready接口处理接下来的数据(关于这部分的介绍,文档中很详细,相信大家一看便知,就不多说了。)

  • 后言:

关于掉微信分享接口,其实这个功能应该在年前就做好的,只是当时也是因为签名总是获取失败的原因而将此搁置,直至今天不得不重新开始研究,突然明白了“凡是出来混,总是要还的”这句至理名言的真理性。而自己遇到的这个问题,也希望以后不会再犯,还有可以用到的小伙伴们也能用上。

关于微信的jsdk的若干亲身实践之小结的更多相关文章

  1. 亲身实践 yui-compressor压缩js和css

    最近很懒散,个人感情.家庭原因,没有动力去学东西,老是发誓要搞好前端工程化,老中断,唉!没有魄力! 最近老觉得这前端工程化有什么好的,东西那么多,还得学!直到前几天产品提了个优化,说搜索结果页跳商品详 ...

  2. 微信团队原创分享:iOS版微信的内存监控系统技术实践

    本文来自微信开发团队yangyang的技术分享. 一.前言 FOOM(Foreground Out Of Memory),是指App在前台因消耗内存过多引起系统强杀.对用户而言,表现跟crash一样. ...

  3. 微信小程序开发入门与实践

    基础知识---- MINA 框架 为方便微信小程序开发,微信为小程序提供了 MINA 框架,这套框架集成了大量的原生组件以及 API.通过这套框架,我们可以方便快捷的完成相关的小程序开发工作. MIN ...

  4. 微信小程序从入门到实践(一)-设置底部导航栏

    微信小程序最多能加5个导航图标.因为我们只有两个默认页面,这里我们就添加两个导航图标 先看我们要达到的就是这么一个效果 接下来开始实践: (1)准备工作 找几个图标,将上述起好名字的图标 保存到 小程 ...

  5. 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...

  6. 微信分享jsdk接口

    HTML文件 <!DOCTYPE html><html><head> <meta charset="utf-8"> <titl ...

  7. 微信小程序的初窥实践

    最近,小程序正式上线,各企业都纷纷开发,本博主看下其中奥秘, 首先得去微信公众平台(https://mp.weixin.qq.com/)注册一个小程序账号(以前注册过公众号的账号不可使用) 备注:注册 ...

  8. 微信支付之手机H5支付实践

    最近项目中支付部分涉及到微信支付,使用的是h5支付,官方文档中是没有demo的,所以摸着石头过河,将踩过的坑记录如下. 一 应用场景 H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前 ...

  9. 微信小程序“信用卡还款”项目实践

    小程序概述 11月3日晚,微信团队对外宣布,微信小程序开放公测.开发者可登陆微信公众平台申请,开发完成后可以提交审核,公测期间暂不能发布. 我们前一段时间也进行了小程序开发,现在来对之前的开发体验做一 ...

随机推荐

  1. RSA 算法-MSDN文档

    RSA 算法 若要生成密钥对,可以从创建名为 p 和 q 的两个大的质数开始. 这两个数相乘,结果称为 n. 因为 p 和 q 都是质数,所以 n 的全部因数为 1. p. q 和 n. 如果仅考虑小 ...

  2. tipask 不能正常解析

    <? if(!defined('IN_TIPASK')) exit('Access Denied'); include template('header'); ?> 代码如上,经查询为ph ...

  3. iOS键盘类型以及样式展示

    UIKeyboardTypeDefault: UIKeyboardTypeASCIICapable: UIKeyboardTypeNumbersAndPunctuation: UIKeyboardTy ...

  4. JDBC的介绍2

    一.基础知识 1. 数据持久化 持久化(persistence):对象在内存中创建后,不能永久存在.把对象永久的保存起来就是持久化的过程.而持久化的实现过程大多通过各种关系数据库来完成. 持久化的主要 ...

  5. Lo4j(一)初识

    最近开始在研究log4j,可能因为是想要自己去搭建框架那. 废话不多说,先上一个例子好了. 第一步:当然是引入对象的jar包了 地址:http://www.apache.org/dyn/closer. ...

  6. laravel 连接mongodb

    In this article we will see how to use MongoDB with Laravel (PHP framework). So first we need to ins ...

  7. ubuntu 14.04 安装中文输入法

    记录Ubuntu 14.04 里面安装中文输入法的过程 先安装如下包 sudo apt-get install ibus sudo apt-get install ibus ibus-clutter ...

  8. QT 5.7.0 交叉编译记录

    这一篇记录 Qt 5.x cross-compiler with eglfs , 平台是 TI-AM3354, 上一篇SGX的移植就是为了这一次的交叉编译. 一. 下载QT的源码: 地址: http: ...

  9. JavaScrip——DOM操作(查找HTML元素/修改元素)

    innerHTML 1.查找元素——document.getElementById("intro") 2.输出查找的结果: (1)var a=document.getElement ...

  10. 网卡phy9161A

    硬件1. 网口网口使用4根信号线:两根发送,两根接收.一对信号线中一根承载0——+2.5V信号电压,而另一根负载的电压是0——-2.5V,因此可产生一个5Vpp的信号差.RJ45中有用的就是4根信号线 ...