关于微信的jsdk的若干亲身实践之小结

    • 前言:

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

Screenshot_2017-04-18-11-32-30_[B@2f9f4a9

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

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

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

为规范自定义分享连接功能在网页上的使用,自2017年4月25日起,JSSDK“分享到朋友圈”及“发送给朋友”接口,自定义的分享连接,其域名或路径必须与当前页面对应的公众号JS安全域名一致,不然将调用失败。node

例如,当前页面是 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 均将失败。ajax

对于未接入微信JSSDK或已接入但JSSDK调用失败的网页,被用户分享时,分享卡片将统一使用默认缩略图和标题简介,不容许自定义。redis

接口完整用法请参考《微信JSSDK说明文档》,请开发者及时完成调整。”)算法

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

  • 过程:

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

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

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

<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        //当前时间戳  这里是秒级别的时间戳格式

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

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

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页面代码:

<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script>
    jQuery(function () {
       //页面初始化完成后,先post请求将当前页面的url传到后台,进行signature的计算,而这里必定记得要: encodeURIComponent()一下,去后台后在: decodeURIComponent(),要不就是频繁报“invalid signature"的错误
        jQuery.post("/share/getshare", {"url": encodeURIComponent(window.location.href.split('#')[0]),"t": new Date().getTime()}, function (result) {
            if (result.errno != 0) {
                alert("您当前的网络不稳定请稍后再试!");
                return;
            }
            var shareUrl = result.data.url;
            wx.config({
                debug: false, // 开启调试模式,调用的全部api的返回值会在客户端alert出来,若要查看传入的参数,能够在pc端打开,参数信息会经过log打出,仅在pc端时才会打印。
                appId: 'wxf919e3b61eca36ae', // 必填,公众号的惟一标识
                timestamp: result.data.timestamp, // 必填,生成签名的时间戳
                nonceStr: result.data.nonceStr, // 必填,生成签名的随机串
                signature: result.data.signature,// 必填,签名,见附录1
                jsApiList: ['onMenuShareAppMessage','onMenuShareTimeline','onMenuShareQQ','onMenuShareWeibo'] // 必填,须要使用的JS接口列表,全部JS接口列表见附录2
            });
            wx.ready(function () {
                var title = "{{data.title}}";
                var desc = "{{data.scontent}}"?"{{data.scontent}}":"";
                var imgUrl = "http://static.gangguwang.com/image/2016/12/16/16/55/5853abfc101887000800111f.jpg";
                wx.onMenuShareTimeline({//“分享到朋友圈”
                    title: title, // 分享标题
                    link: shareUrl, // 分享连接
                    imgUrl: imgUrl, // 分享图标
                    success: function () {
                        // 用户确认分享后执行的回调函数
                    },
                    cancel: function () {
                        // 用户取消分享后执行的回调函数
                    }
                });
                wx.onMenuShareAppMessage({//“分享给朋友”
                    title: title, // 分享标题
                    desc: desc, // 分享描述
                    link: shareUrl, // 分享连接
                    imgUrl: imgUrl, // 分享图标
                    type: 'link', // 分享类型,music、video或link,不填默认为link
                    dataUrl: '', // 若是type是music或video,则要提供数据连接,默认为空
                    success: function () {
                        console.log('succ~');
                        // 用户确认分享后执行的回调函数
                    },
                    cancel: function () {
                        console.log('fail~');
                        // 用户取消分享后执行的回调函数
                    }
                });
                wx.onMenuShareQQ({//“分享到QQ”
                    title: title, // 分享标题
                    desc: desc, // 分享描述
                    link: shareUrl, // 分享连接
                    imgUrl: imgUrl, // 分享图标
                    success: function () {
                        // 用户确认分享后执行的回调函数
                    },
                    cancel: function () {
                        // 用户取消分享后执行的回调函数
                    }
                });
                wx.onMenuShareWeibo({//“分享到腾讯微博”
                    title: title, // 分享标题
                    desc: desc, // 分享描述
                    link: shareUrl, // 分享连接
                    imgUrl: imgUrl, // 分享图标
                    success: function () {
                        // 用户确认分享后执行的回调函数
                    },
                    cancel: function () {
                        // 用户取消分享后执行的回调函数
                    }
                });
            });
        });
    })
</script>
后台的处理逻辑:
//这里先要引入sha1依赖包 
//getshare方法即是得到动态签名的方法(这是基于node.js环境下的)
async getshareAction(){
        //将前台获得的url先decodeURIComponent()一下再使用
        let url = decodeURIComponent(this.post().url);
        //当前时间戳
        let timestamp = parseInt( new Date().getTime()/1000);
        //随机字符串
        let nonceStr = Math.random().toString(36).substr(2,16);
        ////获取缓存信息 存在redis中,从 redis 里获取缓存(全局缓存jsapi_ticket   ----->   有效期7200秒)
        let ticket = await think.cache('ticket_weixinshare_ywg', undefined, {type: 'redis'});
        let access_token = await think.cache('token_weixinshare_ywg', undefined, {type: 'redis'});
        let req = think.promisify(request.get);
        if(!access_token){//不存在
            let options1 = {//获取 acess_token
                url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=appid&secret=secret',
                method: "get"
            };
            let res = await req(options1);
            access_token = JSON.parse(res.body).access_token;
            //设置缓存  access_token(这里是thinkjs的redis设置缓存的方法)
            await think.cache('token_weixinshare_ywg', access_token);
        }
        if(!ticket){
            let options2={//计算 jsapi_ticket
                url: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+access_token+'&type=jsapi',
                method: "get"
            };
            let res2 = await req(options2);
            if(JSON.parse(res2.body).errcode==0){
                ticket=JSON.parse(res2.body).ticket;
                //设置缓存 ticket
                await think.cache('ticket_weixinshare_ywg', ticket);
            }else{
                console.log('=========error==='+JSON.parse(res2.body).errmsg)
            }
        }
        //按照字段名的ASCII 码从小到大排序(字典序)
        let raw = async (args) =>{
            let keys = Object.keys(args);
            keys = keys.sort();
            let newArgs = {};
            keys.forEach(function (key) {
                newArgs[key.toLowerCase()] = args[key];
            });
            let string = '';
            for (let k in newArgs) {
                string += '&' + k + '=' + newArgs[k];
            }
            string = string.substr(1);
            return string;
        };
        let ret = {
            jsapi_ticket: ticket,
            nonceStr: nonceStr,
            timestamp: timestamp,
            url: url
        };
        let string1 = await raw(ret);
        //将获得的字符串进行sha1加密,而后返回将 wx.config()中所须要的值返回到前台页面
        let signature = sha1(string1);
        this.success({"timestamp":timestamp,"nonceStr":nonceStr,"signature":signature,"url":url});
    }

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

微信图片_20170419173841

而昨天一成天的状况即是:

微信图片_20170419174040

而对于“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接口处理接下来的数据(关于这部分的介绍,文档中很详细,相信你们一看便知,就很少说了。)

微信图片_20170419181037

  • 后言:

       关于掉微信分享接口,其实这个功能应该在年前就作好的,只是当时也是由于签名老是获取失败的缘由而将此搁置,直至今天不得不从新开始研究,忽然明白了“凡是出来混,老是要还的”这句至理名言的真理性。而本身遇到的这个问题,也但愿之后不会再犯,还有能够用到的小伙伴们也能用上。

相关文章
相关标签/搜索