【前言】 某天,接到這么一個(gè)需求:自定義微信網(wǎng)頁(yè)分享出來(lái)的標(biāo)題贺拣,描述和圖標(biāo)蓖谢。以前沒(méi)玩過(guò)這個(gè),感覺(jué)應(yīng)該很簡(jiǎn)單譬涡,動(dòng)手了之后闪幽,躺過(guò)各種坑才知道并沒(méi)那么容易。完全獨(dú)立研究排錯(cuò)涡匀,感受頗多盯腌,分享出來(lái)給大家鋪一鋪路
一: 需求來(lái)源
?開(kāi)發(fā)了一個(gè)移動(dòng)端H5活動(dòng)頁(yè)面,該頁(yè)面要實(shí)現(xiàn)微信中的“分享給好友”陨瘩,“分享到朋友圈”腕够,“分享到QQ”级乍,“分享到騰訊微博”等功能。如果沒(méi)有接入jssdk燕少,分享出來(lái)的就是如下樣子:
?除了標(biāo)題比較容易自定義以外卡者,描述內(nèi)容和圖標(biāo)都是默認(rèn)樣式,好丑客们。如果就這么分享出去崇决,活動(dòng)本身的吸引力就會(huì)被大打折扣,所以底挫,這個(gè)功能必須完成恒傻!
?
二: 需要工具
?我自己在開(kāi)發(fā)的時(shí)候,不知道需要這些東西建邓,都是遇到一個(gè)坑盈厘,才去找的一個(gè)蘿卜;這里提前提到官边,帶你走捷徑:
- 微信公眾平臺(tái)技術(shù)文檔 - 必備沸手,接入微信的功能,一切都是從這里開(kāi)始
- 微信JS接口簽名校驗(yàn)工具 - 該工具能幫你核查生成的簽名的正確性
-
微信web開(kāi)發(fā)者工具 - 模擬web網(wǎng)頁(yè)調(diào)試微信接口注簿,能查看JS-SDK的狀態(tài)契吉,查看權(quán)限列表,還能接入移動(dòng)設(shè)備調(diào)試诡渴;完美解決微信接口調(diào)試難的問(wèn)題捐晶。獲取方法:
登錄微信公眾平臺(tái) -> 進(jìn)入開(kāi)發(fā)者工具 -> 下載微信web開(kāi)發(fā)者工具
下載后,使用方法和chrome類似妄辩,不多說(shuō)惑灵。 -
Xshell - 用來(lái)部署服務(wù)端代碼(linux環(huán)境),因?yàn)榻尤胛⑿拍K必須是線上聯(lián)調(diào)的眼耀,所以你的服務(wù)端都是需要部署到線上英支,即使是聯(lián)調(diào)服務(wù)器,也要滿足外網(wǎng)能訪問(wèn)哮伟。當(dāng)然潭辈,如果服務(wù)端人員能幫你解決這個(gè)問(wèn)題,請(qǐng)無(wú)視澈吨。
?
三: 開(kāi)發(fā)前需要
1. 綁定域名
?登錄微信公眾平臺(tái)(真實(shí)運(yùn)營(yíng)的服務(wù)號(hào)),進(jìn)入“公眾號(hào)設(shè)置”寄摆,選擇導(dǎo)航標(biāo)簽中的“功能設(shè)置”谅辣,填寫(xiě)“JS接口安全域名”。注意婶恼,該頁(yè)上也有描述桑阶,需要下載一個(gè) .txt
文件放到你網(wǎng)頁(yè)(你所填寫(xiě)的安全域名)要部署的服務(wù)器的根目錄(或目錄路徑)下柏副,這個(gè)文件是微信用來(lái)檢測(cè)你的服務(wù)器是否可達(dá)使用,不同公眾號(hào)的txt
文件不同蚣录,我就不小心用放在桌面上的自己的公眾號(hào)的測(cè)試文件放到服務(wù)器中割择,然后以公司的服務(wù)號(hào)去跑,一直通不過(guò)萎河。
?服務(wù)號(hào)才有對(duì)應(yīng)的接口權(quán)限荔泳,如果你綁定了一個(gè)私人的公眾號(hào),一樣沒(méi)法使用虐杯。你可以“開(kāi)發(fā)者中心”查看一下你是否擁有對(duì)應(yīng)接口的開(kāi)發(fā)權(quán)限玛歌,免得白忙活。
2. 引入 JS 文件
? 和其他的模塊一致擎椰,想要調(diào)用微信的功能支子,同樣也需要引入微信js文件,目前最新版本為1.2.0达舒;該文件有 http形式 和 https形式值朋,下載之后在需要調(diào)用js接口的頁(yè)面引入。也可以全局引入巩搏,已經(jīng)壓縮過(guò)昨登,大小為11.5k,還算小塔猾。
?模塊化推薦使用 AMD/CMD 標(biāo)準(zhǔn)加載
?
四: 開(kāi)始開(kāi)發(fā)
1. 權(quán)限驗(yàn)證配置
?有了上述的準(zhǔn)備篙骡,我們可以通過(guò)config
在要使用JS-SDK的頁(yè)面注入配置信息,這是你調(diào)用微信接口微信驗(yàn)證的入口丈甸。config
接收一個(gè)對(duì)象糯俗,具體如下:
即:
wx.config({
debug: false, // 是否打開(kāi)調(diào)試模式,調(diào)用的api會(huì)被alert出來(lái)睦擂,在pc端也能看到log信息
appid:''得湘, // 必填,微信公眾號(hào)的唯一標(biāo)識(shí)
timestamp: , // 必填顿仇,生成簽名的時(shí)間戳
nonceStr: '', // 必填淘正,生成簽名的隨機(jī)串
signature: '', //必填,用于驗(yàn)證的簽名
jsApiList: [] //必填臼闻,需要使用到的JS接口列表
})
?大部分文檔都這么一說(shuō)而過(guò)鸿吆,然而還是容易踩坑。
?糞坑: 為什么叫糞坑述呐,因?yàn)樽銐虻椭巧坛痛尽R婚_(kāi)始沒(méi)對(duì)微信公眾號(hào)的這些機(jī)制了解多少,直接用appid
, appSecret
生成靜態(tài)的accesstoken
乓搬,然后用accesstoken
生成了靜態(tài)的jsapi_ticket
思犁,最后生成靜態(tài)的signature
代虾,然后就把這幾個(gè)參數(shù)填進(jìn)去了,肯定錯(cuò)啊激蹲。這些信息除了id
和secret
其他都是動(dòng)態(tài)的棉磨。
?坑1: 可以從上圖中看出來(lái),我直接設(shè)置debug
參數(shù)為false
学辱,并不是我覺(jué)得多余乘瓤,而是根本看不明白,本地調(diào)試基本沒(méi)法看出問(wèn)題项郊。怎么辦馅扣? 在 “需要工具” 中提供的微信web開(kāi)發(fā)者工具,他由微信團(tuán)隊(duì)提供着降,用了才知道差油,要啥debug調(diào)試,這個(gè)工具簡(jiǎn)直完美任洞,所有信息展示 清清楚楚蓄喇,一目了然。
?坑2: timestamp
配置項(xiàng)處沒(méi)有引號(hào)交掏,也就是說(shuō)妆偏,這里的類型是 INT。另外盅弛,還有一個(gè)很重要的就是钱骂,這里 單位是秒,相信絕大多數(shù)一開(kāi)始和我一樣挪鹏,以為是毫秒见秽,直接new Date().getTime()
完事。踩了踩了讨盒。
?坑3:nonceStr
必須遵循駝峰式命名規(guī)則解取。這里比較容易混淆是因?yàn)樵谏珊灻且徊剑枰冉M成字符串string1
返顺,那里要求所有key
包括noncestr
也都是小寫(xiě)的禀苦,所以誤以為這里也是小寫(xiě)的。
?坑4:或許你有看到jsApiList
中有的文檔有checkJsApi
項(xiàng)遂鹊,有的又沒(méi)有振乏。是這樣的:在真正調(diào)用微信接口功能如“分享到朋友圈”之前,我們可以對(duì)微信接口先進(jìn)行驗(yàn)證接口是否可用秉扑,即昆码,checkJsApi
是一個(gè)基礎(chǔ)接口,所以也要加入該數(shù)組中,如下
?為了不打亂思路赋咽,這里將如何獲取簽名過(guò)程放在最后面。因涉及太多重要信息吨娜,這個(gè)步驟要求必須在服務(wù)端實(shí)現(xiàn)脓匿。
2. ready驗(yàn)證成功回調(diào)
?config
信息驗(yàn)證成功后,下一步就是使用微信的各種接口宦赠。微信提供config
的回調(diào)函數(shù)ready()陪毡,供我們執(zhí)行這些接口。注意:config
是一個(gè)客戶端的異步操作勾扭,所以如果需要在頁(yè)面加載時(shí)就調(diào)用相關(guān)接口毡琉,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來(lái)確保正確執(zhí)行。但是如果是一些交互操作妙色,這不必寫(xiě)在ready中桅滋,可以直接調(diào)用。比如說(shuō)“發(fā)送給好友”就是與用戶交互后的操作身辨。
3. error驗(yàn)證失敗處理
wx.error(function(res){
console.log(res); // res為微信返回的錯(cuò)誤結(jié)果
})
4. 自定義分享內(nèi)容
?這里我定義了我們需要分享的一些內(nèi)容丐谋,如, 標(biāo)題煌珊,內(nèi)容描述号俐,和圖片地址。
坑:這里的圖片地址必須是絕對(duì)地址定庵。上圖中的
data.url
是發(fā)送給微信校驗(yàn)的那個(gè)url
.
4. 定義分享接口
?這里只寫(xiě)了幾個(gè)最常用的接口吏饿,如需要分享西,
- 分享到朋友圈 - onMenuShareTimeLine()
接受一個(gè)對(duì)象蔬浙,分享到朋友圈需要自定義三個(gè)內(nèi)容猪落,展示圖標(biāo)imgUrl
,標(biāo)題title
和鏈接地址link
敛滋。
wx.onMenuShareTimeline({
title: '', // 分享標(biāo)題
link: '', // 分享鏈接许布,該鏈接域名或路徑必須與當(dāng)前頁(yè)面對(duì)應(yīng)的公眾號(hào)JS安全域名一致
imgUrl: '', // 分享圖標(biāo)絕對(duì)路徑
success: function () {
// 用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù)
},
cancel: function () {
// 用戶取消分享后執(zhí)行的回調(diào)函數(shù)
}
});
- 發(fā)送給好友 - onMenuShareAppMessage()
?“發(fā)送給好友”需要自定義四個(gè)信息,標(biāo)題title
绎晃,描述desc
蜜唾,圖標(biāo)絕對(duì)地址imgUrl
和跳轉(zhuǎn)的link
- 分享到QQ - onMenuShareQQ()
?自定義四個(gè)信息:標(biāo)題title
,描述desc
庶艾,跳轉(zhuǎn)鏈接link
袁余,和圖標(biāo)絕對(duì)地址Url
- 分享到QQ空間 - onMenuShareQZone()
wx.onMenuShareQZone({
title: title,
desc: desc,
link: link,
imgUrl: imgUrl,
success: function () {
// 用戶確認(rèn)分享后執(zhí)行的回調(diào)函數(shù)
},
cancel: function () {
// 用戶取消分享后執(zhí)行的回調(diào)函數(shù)
}
});
?
JS-SDK使用權(quán)限簽名算法
?很高大上的名字,聽(tīng)著怪嚇人咱揍。其實(shí)只要幾個(gè)參數(shù)給對(duì)了颖榜,還是很容易處理的。需要注意的是,這個(gè)步驟需要在服務(wù)端實(shí)現(xiàn)
?上面“糞坑”提到的掩完,雖然校驗(yàn)是失敗的噪漾,但步驟就是如此了。這里再概括一下:
登錄微信公眾號(hào) →
拿到appId和appSecret →
用他們?nèi)ノ⑿奴@取access_token →
用access_token去獲取jsapi_ticket →
生成一個(gè)隨機(jī)字符串noncestr →
生成當(dāng)前時(shí)間戳且蓬,單位是秒 →
用隨機(jī)jsapi_ticket欣硼,當(dāng)前時(shí)間戳,隨機(jī)字符串以及分享的url用&拼接成string1 →
對(duì)string1進(jìn)行sha1加密 →
加密后的字符串就是signature →
數(shù)據(jù)返回客戶端
?先在腦海形成一個(gè)思路恶阴。我們?cè)趤?lái)細(xì)看每一步:
1. 獲取access_token
?微信提供access_token
的有效期是兩個(gè)小時(shí)诈胜,即7200秒,超時(shí)失效冯事;且下一次獲取也會(huì)使上次獲取的結(jié)果失效焦匈。所以我們需要對(duì)它進(jìn)行動(dòng)態(tài)獲取。目前微信支持每個(gè)公眾號(hào)每天最多2000次的access_token
獲取請(qǐng)求昵仅。我設(shè)定7000秒去更新一次缓熟,這里用的nodejs實(shí)現(xiàn),其實(shí)思路都是一樣的岩饼。具體實(shí)現(xiàn)如下:
?這里先校驗(yàn)了本地文件
accesss_token.json
中的access_token
字段是否為空荚虚,為空表示是第一次獲取。再利用下面語(yǔ)句判斷本地的是否已經(jīng)過(guò)了我們所設(shè)定的有效期:
accessTokenJson.expire_time < currentTime
如果滿足條件籍茧,就重新發(fā)起requestGet請(qǐng)求重新獲取版述,否則從本地拿。
?為什么要這么做寞冯? 因?yàn)閷?duì)外的活動(dòng)渴析,如果用戶量比較大,每一個(gè)用戶都去獲取一次吮龄,微信限定的訪問(wèn)次數(shù)在2000個(gè)訪問(wèn)中就用完了俭茧,接下來(lái)當(dāng)天的訪問(wèn)將再也通不過(guò)簽名驗(yàn)證。
2. 生成 jsapi_ticket
?生成jsapi_ticket
的步驟和獲取access_token
的思路一致漓帚,具體如下所示:
?這里需要注意的是母债,不僅要判斷
ticket
是否為空,還要判斷token
是否失效尝抖。因?yàn)楫?dāng)token
提前失效后ticket
將變得無(wú)意義.?另外還要考慮這種情況毡们,某次請(qǐng)求獲取到一個(gè)
token
后,我們并不能保證其他人員不會(huì)去微信公眾號(hào)或者web url
等方式去請(qǐng)求獲取token
昧辽,這將導(dǎo)致因token
被更新而上次的token
失效衙熔,但我們的程序并不知道,繼續(xù)使用“上一次請(qǐng)求”的token
搅荞,在下一個(gè)“重新請(qǐng)求”前將一直是失效的红氯,不要漏了這種情況框咙。
3. 生成隨機(jī)字符串
?傳給randomString
一個(gè)長(zhǎng)度參數(shù),生成所需要的隨機(jī)長(zhǎng)度字符串痢甘。我這里設(shè)定的是長(zhǎng)度為16
喇嘱,代碼如下,簡(jiǎn)單不解釋塞栅。
4. 獲取JS-SDK簽名
?公式:signature=sha1(string1)
婉称;即,需要生成string1
构蹬,再對(duì)string1
進(jìn)行sha1
加密得到signature
?微信文檔中提到,
參與簽名的字段包括noncestr(隨機(jī)字符串), 有效的jsapi_ticket, timestamp(時(shí)間戳), url(當(dāng)前網(wǎng)頁(yè)的URL悔据,不包含#及其后面部分) 庄敛。對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1.這里需要注意的是所有參數(shù)名均為小寫(xiě)字符科汗。對(duì)string1作sha1加密藻烤,字段名和字段值都采用原始值钞护,不進(jìn)行URL 轉(zhuǎn)義隙咸。
?其實(shí)簡(jiǎn)單了來(lái)說(shuō),string1
就是jsapi_ticket=它的值&noncestr=隨機(jī)字符串×tamp=當(dāng)前時(shí)間戳(單位秒)&url=當(dāng)前網(wǎng)頁(yè)url
挚瘟。微信文檔說(shuō)要按字段名的字典序排序坤检,請(qǐng)注意兴猩,是按字段名的字典序,因?yàn)樽侄蚊蛔冊缧哉f(shuō)其實(shí)這個(gè)串中的各個(gè)參數(shù)順序是不變的倾芝。
【填坑】:
- 簽名用的
noncestr
和timestamp
必須與wx.config
中的nonceStr
和timestamp
相同。 - 簽名用的
url
必須是調(diào)用JS接口頁(yè)面的完整URL(請(qǐng)用location.href.split('#')[0]
)確認(rèn)箭跳,包括'http(s)://'
部分晨另,以及'?'
后面的GET
參數(shù)部分,但不包括'#'hash
后面的部分)
?能躺的坑基本已經(jīng)說(shuō)明谱姓,如有錯(cuò)誤或缺漏的地方借尿,歡迎交流。