前言
自己開始開發(fā)的時候也在網(wǎng)上搜過些教程,尤以segmentfault和腳本之家的兩篇文章為甚,然后兩篇文章都只是講了自己的場景和如何使用,卻沒有講述其中的原理。我不喜歡只會用的程度,如果不明白為什么這么做,每一步做的理由,所以寫下這篇文章,分享一下我在開發(fā)中的心得。
前期準(zhǔn)備
你肯定不愿意每次都在手機(jī)上調(diào)試,因?yàn)椴荒躢onsole,所以你得準(zhǔn)備這個渣渣開發(fā)者工具,mac下容易假死。有大佬改chrome,偽裝成微信瀏覽器,我沒試過,不知道能不能成功。
然后是natapp,或許大家會有疑問,我都有服務(wù)器了,還買這個穿透工具干啥?因?yàn)槲⑿藕炞C和當(dāng)前瀏覽器URL有關(guān)(微信設(shè)置JS安全域名),你肯定不想每次打包后都拜托后臺大哥傳上去你再調(diào)試吧。但是本地的localhost 192.168.x.x是無效的,因此通過natapp把當(dāng)前主機(jī)暴露到外網(wǎng),這樣可以臨時設(shè)置JS安全域名到這里,就可以進(jìn)行驗(yàn)簽和授權(quán)了。
額外補(bǔ)充
我們知道,做Vue開發(fā)會通過express開啟一個臨時服務(wù)器,我試過natapp直接映射到這個服務(wù)器,前端請求會報錯,content-length not match。所以我就用anywhere對打包后的文件又開了一個本地服務(wù)器,這樣其實(shí)效率挺低的,希望哪位大佬告訴我解決方案。
渣渣mac
該問題存在于mac下,windows下無問題。
網(wǎng)頁授權(quán)和分享
這倆貨其實(shí)是不一樣的,得分開實(shí)現(xiàn),網(wǎng)頁授權(quán)是一套機(jī)制。分享是另一套機(jī)制。我們先看看分享
微信分享
首先綁定域名,這個就填natapp的臨時域名就好了。然后引入JS文件,npm上有現(xiàn)成的包
import wx from 'weixin-js-sdk';
,第三個就是最重要的了。debug參數(shù)開啟了,你的驗(yàn)簽信息,分享結(jié)果,成功與失敗都會以alert形式的彈窗彈出來了。調(diào)試的階段我們需要打開,接著幾個參數(shù)都沒太大的問題,唯一難點(diǎn)在于signature。簽名
signature
這些參數(shù)都應(yīng)該初始化過程中請求后臺,由后臺返回。值得注意的是signature,在附錄中有詳細(xì)的介紹。這個signature會和當(dāng)前瀏覽器的URL有關(guān),并且注冊以后如果URL發(fā)生改變需要重新注冊。什么意思呢?
vue-router中如果mode設(shè)置為history模式,如果進(jìn)入不同路由時,URL發(fā)生了變化,此時微信認(rèn)為你的URL是不合法的,因此驗(yàn)簽失效了,你就得必須重新驗(yàn)簽。
如果每一個路由都重新驗(yàn)簽對于我這種強(qiáng)迫癥是受不了的。因此我們使用hash模式,這樣路由會響應(yīng)#號后面的變化,真正的地址沒有發(fā)生變化。因此只需要一處驗(yàn)簽,后面都不會受到影響。
下面是我的驗(yàn)簽代碼
首先發(fā)出一段ajax請求,講當(dāng)前的URL傳遞給后臺,后臺會根據(jù)這個URL生成signature,注意這里使用的是
window.location.href.split('#')[0]
方法,不能寫死。因?yàn)槿绻M(jìn)行網(wǎng)頁授權(quán)的話。query參數(shù)會出現(xiàn)在#號前面。比如http://xxx.xx.com/?xx=xx&xx=xx&/#/xxx
,這個query參數(shù)將會伴隨整個頁面的生命周期。并且其中的參數(shù)是動態(tài)變化的,如果寫死將會導(dǎo)致驗(yàn)簽失敗。ajax拿到后臺數(shù)據(jù)后進(jìn)行驗(yàn)簽,成功后調(diào)試模式下會有提示。接著進(jìn)行分享
如果出現(xiàn)正確的圖片和title,并且調(diào)試信息無誤的話一個分享就成功了。值得注意的是,分享出去的URL必須和簽名的URL一樣,否則分享會失敗(我試過分享授權(quán)的鏈接,失敗了)。并且如果當(dāng)前用戶沒有訂閱的話,分享出去也只是title和一串URL地址,只有訂閱用戶才能分享出圖片和描述。
在頻繁改動appid的時候(測試需要),有的時候微信服務(wù)器會有緩存,導(dǎo)致正確的簽名順序也會報錯誤的驗(yàn)簽,這個時候等等就好了。
目前只做了風(fēng)險,驗(yàn)簽和分享坑差不多就這些。
網(wǎng)頁授權(quán)
這個其實(shí)很簡單,就是誘導(dǎo)一個用戶跳轉(zhuǎn)到一個網(wǎng)頁,這個網(wǎng)頁的URL配置了一些參數(shù),按照相關(guān)的要求由后臺配好以后點(diǎn)進(jìn)去就行了。靜默授權(quán),就是不獲取用戶的信息,表現(xiàn)就是鏈接跳出去又跳回來。獲取信息就是用戶點(diǎn)擊一個確認(rèn)授權(quán),然后你就可以獲取該用戶的相關(guān)信息了。
這個示例鏈接是
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
其中的redirect_uri就是回調(diào)的URL,授權(quán)成功后,將轉(zhuǎn)入這個頁面,并且攜帶兩個query參數(shù),其中的code就是授權(quán)碼,通過這個授權(quán)碼獲取該用戶的openid。
這個授權(quán)碼有時間限制,并且只能使用一次。將這個授權(quán)碼發(fā)送給后臺以后,后臺請求openid,這個openid是唯一的,可以通過這個openid在數(shù)據(jù)庫中綁定用戶。之后的邏輯就和非微信環(huán)境沒有太大區(qū)別了。
業(yè)務(wù)邏輯
整個業(yè)務(wù)邏輯就是,首先判斷是不是微信環(huán)境(我們的網(wǎng)頁要求移動端同樣能用)
const isWechat = () => {
let ua = window.navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) == 'micromessenger';
}
如果是微信環(huán)境,就對他進(jìn)行驗(yàn)簽。此時并未網(wǎng)頁授權(quán),網(wǎng)頁授權(quán)和驗(yàn)簽可以分開。
授權(quán)的邏輯放在開啟探索這個按鈕上
首先是判斷當(dāng)前是否存在code(因?yàn)檫@可能已經(jīng)是授權(quán)后的頁面,因此先檢查是否有code,再判斷是否需要跳轉(zhuǎn)授權(quán)頁)
const getQuery = name => {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
如果code存在就把code發(fā)給后臺,根據(jù)后臺的返回結(jié)果處理自己的業(yè)務(wù)邏輯。code不存在則通過
window.location.href=xxxxx
跳轉(zhuǎn)到微信授權(quán)頁,授權(quán)成功后又跳轉(zhuǎn)到當(dāng)前頁面,此時code存在。
如果需要其他JSSDK服務(wù),就讓后臺吧通過code獲取的openid,accesstoken保存下來。有些用戶已經(jīng)使用過了網(wǎng)站服務(wù),或者網(wǎng)站需要用戶的手機(jī)號,獲取code以后就跳轉(zhuǎn)一個綁定頁,要求用戶綁定手機(jī)或者郵箱,這個地方根據(jù)自己的業(yè)務(wù)邏輯來。
因?yàn)橐瑫r處理微信與非微信登錄,所以這里的判斷邏輯比較復(fù)雜,建議先草稿紙規(guī)劃好,考慮到每一個情況再敲代碼。不然到時候甩鍋都甩不好。
結(jié)束
微信開發(fā)確實(shí)是一個不小的難題,并非代碼多高深,主要難在調(diào)試的環(huán)境上。每一個域名綁定,授權(quán)都很復(fù)雜。相關(guān)的文檔個人覺得也并不完善。并且最坑爹的是微信會緩存appid,這樣在開發(fā)切換到生產(chǎn)環(huán)境的時候,微信服務(wù)器得appid并沒有切換成功,導(dǎo)致驗(yàn)簽失敗。在你確認(rèn)簽名配置無誤的時候,依舊發(fā)生驗(yàn)簽失敗,那么你就等等吧。
另外,未驗(yàn)簽分享出去的title默認(rèn)就是網(wǎng)頁的title,也就是document.title。
:)就是這樣