關於微信的jsdk的若干親身實踐之小結


    • 前言:

業務來源:自主研發的手機app軟件有分享文章到微信或者QQ以及微博的功能,而在微信中再次點擊分享按鈕的時候,情況就出現的不可把控了:

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

          文章顯示的縮略圖不能正常顯示;文章的簡介不能顯示……而我們領導的要求便是再次分享的時候,顯示自己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接口安全域名,即程序運行的網址是什么,需要下載的文件,官網上直接下載配置即可;配置完成后,可在“開發者中心”查看對應的接口權限); 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

  • 后言:

       關於掉微信分享接口,其實這個功能應該在年前就做好的,只是當時也是因為簽名總是獲取失敗的原因而將此擱置,直至今天不得不重新開始研究,突然明白了“凡是出來混,總是要還的”這句至理名言的真理性。而自己遇到的這個問題,也希望以后不會再犯,還有可以用到的小伙伴們也能用上。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM