幾乎每一個開發用於微信公衆號頁面的工程師都遇到過微信jssdk
報的各種錯誤,通常是permission denied
,類似這樣:
通常他們會建議你把debug
選項開開,比如這樣:
wechat.config({
debug: true,
appId: appId,
timestamp: timestamp,
nonceStr: nonceStr,
signature: signature,
jsApiList: ['scanQRCode'],
});
結果你又遇到了invalid signature
。類似這樣:
簽名不對,這是什麼意思?可是這簽名是後端給過來的,我怎麼知道它爲什麼不對?後端用的是標準算法,不可能不對啊。
查網上各種教程,或者微信官網,他們總是不厭其煩地告訴你,讓你去檢查簽名算法,然而根本沒有用!
90%
的這種情況下,其實只是一個原因:你用於計算簽名的URL
地址不對,跟算法沒有任何關係,完全不必浪費時間去看什麼簽名算法。
微信要求:如果我們需要在頁面中調用微信的某個方法,則必須用這個頁面的URL
地址獲取簽名。聽上去似乎很好理解,但是實際上URL
地址包含的部分很多,有問號,有#
號,你所要做的是取出#
前面的部分。比如說你的URL
地址是這樣:https://www.abc.com/abc.html?abc=def#xyz
,那麼你用於計算簽名的URL
地址不可以是https://www.abc.com/abc.html
,也不能是https://www.abc.com/abc.html?abc=def#xyz
,而必須只能是https://www.abc.com/abc.html?abc=def
。
如何獲取當前頁面的URL
地址呢?這個很簡單:
let wechaturl = window.location.href.split('#')[0];
然而你以爲事情就這樣結束了?太天真。你的頁面還是無法正常使用微信函數的。
因爲:微信內嵌瀏覽器在iOS
和安卓下的表現不一樣。
在安卓下,你確實用上面的方法是可以調用了。但是在iOS
下,簽名依然失敗!因爲在iOS
下,微信需要你傳遞的是入口URL
,而不是當前頁面的URL
!
比如說,你在微信公衆號的某個菜單鏈接進入了A
頁面,然後從A
頁面的某個鏈接跳轉到B
頁面,然後你在B
頁面獲取簽名,如果是在安卓下,你應該用B
頁面的URL
地址來獲取,但是在iOS
下,你還必須用A
頁面的URL
地址來獲取,否則就還是簽名失敗!
知道了原因,就有很多種解決辦法。
首先,我們可以在入口的A
頁面裏增加這樣的判斷:
if (navigator.userAgent.indexOf('iPhone') !== -1) {
window.wechaturl = window.location + '';
}
然後,在B
頁面需要調用簽名的地方,再增加這樣的判斷:
let wechaturl = window.location.href.split('#')[0];
if (window.wechaturl !== undefined) {
wechaturl = window.wechaturl;
}
這樣我們就有效地區分開了iOS
和安卓。但問題是在iOS
下,如果我的另外一個菜單入口是B
頁面,我從B
頁面跳轉到A
頁面,這時候我的入口鏈接被強制變成了A
頁面,依然會產生簽名失敗的錯誤。
所以我們還需要在微信公衆號的每一個入口菜單鏈接里加一個特殊的參數,例如wechat=1
,變成這樣:https://www.abc.com/abc.html?abc=def&wechat=1
,
然後我們再增加一層判斷,變成這樣:
if (navigator.userAgent.indexOf('iPhone') !== -1) {
if (this.$route.query.wechat !== undefined && this.$route.query.wechat === '1') {
window.wechaturl = window.location + '';
}
}
這裏我用了vue
的寫法,但原理是一樣的。只有我檢測到了wechat
這個參數,我才認爲當前頁面是入口頁面,如果沒有檢測到,則不必強行設置爲入口頁面。
這樣似乎就解決了微信簽名失敗的問題。
但是,我們又遇到了另外一種情況:在微信小程序裏用web-view
內嵌的網頁,在安卓下也報permission denied
和invalid signature
錯誤。不過有了上面的經驗,我們診斷錯誤根源還是URL
入口地址的問題。果然,在安卓下用入口地址獲取簽名成功,而用當前地址獲取簽名失敗,爲此,我們在入口頁面裏再加一個判斷:
if (navigator.userAgent.indexOf('miniProgram') !== -1) {
window.wechaturl = window.location + '';
}
至此,各種簽名錯誤的問題纔算是全部解決。