微信jssdk h5分享

使用微信jssdk進行h5分享

記錄一下自己在開發jssdk過程和遇到的坑,最大的坑我覺得是:後臺返回簽名和驗證工具簽名一致,任然報錯invalid signature,解決方法看文末。

一、首先,要嚴格按照微信文檔步驟執行前面幾個步驟,不然後面你會發現很多莫名其妙的坑。這裏主要是4個步驟:

JSSDK使用步驟

步驟一:綁定域名
先登錄微信公衆平臺進入“公衆號設置”的“功能設置”裏填寫“JS接口安全域名”。

備註:登錄後可在“開發者中心”查看對應的接口權限。

步驟二:引入JS文件
在需要調用JS接口的頁面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

如需進一步提升服務穩定性,當上述資源不可訪問時,可改訪問:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。

備註:支持使用 AMD/CMD 標準模塊加載方法加載

在這裏介紹一種vue文件引入js文件的方法 - - 混入 (mixins)

/* loadExternalAssetMixin.js */

const loadExternalAssetMixin = {
  methods:{
    loadScript(src, callback) {
      if (!(typeof callback === 'function')) {
        callback = function() {}
      }
      var check = document.querySelectorAll(`script[src="${src}"]`)
      if (check.length > 0) {
        check[0].addEventListener('load', function() {
          callback()
        })
        callback()
        return
      }
      var script = document.createElement('script')
      var head = document.getElementsByTagName('head')[0]
      script.type = 'text/javascript'
      script.charset = 'UTF-8'
      script.src = src
      if (script.addEventListener) {
        script.addEventListener('load', function() {
          callback()
        }, false)
      } else if (script.attachEvent) {
        script.attachEvent('onreadystatechange', function() {
          var target = window.event.srcElement
          if (target.readyState === 'loaded') {
            callback()
          }
        })
      }
      head.appendChild(script)
    }
  }
}
export default loadExternalAssetMixin

引入方式:

/* xxx.vue */
<script>
import loadExternalAssetMixin from '../util/loadExternalAssetMixin'
export default {
	mixins: [ loadExternalAssetMixin]mounted () {
		this.loadScript('http://res.wx.qq.com/open/js/jweixin-1.4.0.js', () => {
			// 回調
		})
	}
}
</script>

步驟三:通過config接口注入權限驗證配置
所有需要使用JS-SDK的頁面必須先注入配置信息,否則將無法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來實現web app的頁面會導致簽名失敗,此問題會在Android6.2中修復)。

wx.config({
    debug: true, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
    appId: '', // 必填,公衆號的唯一標識
    timestamp: , // 必填,生成簽名的時間戳
    nonceStr: '', // 必填,生成簽名的隨機串
    signature: '',// 必填,簽名
    jsApiList: [] // 必填,需要使用的JS接口列表
});

簽名算法見文末的附錄1,所有JS接口列表見文末的附錄2

附錄1-JS-SDK使用權限簽名算法

jsapi_ticket

生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常情況下,jsapi_ticket的有效期爲7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。

1.參考以下文檔獲取access_token(有效期7200秒,開發者必須在自己的服務全局緩存access_token)

2.用第一步拿到的access_token 採用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

成功返回如下JSON:

{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}

獲得jsapi_ticket之後,就可以生成JS-SDK權限驗證的簽名了。

簽名算法

簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其後面部分) 。對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這裏需要注意的是所有參數名均爲小寫字符。對string1作sha1加密,字段名和字段值都採用原始值,不進行URL 轉義。

即signature=sha1(string1)。 示例:

noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value

步驟1. 對所有待簽名參數按照字段名的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
步驟2. 對string1進行sha1簽名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

注意事項

1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。

2.簽名用的url必須是調用JS接口頁面的完整URL。

3.出於安全考慮,開發者必須在服務器端實現簽名的邏輯。

如出現invalid signature 等錯誤詳見附錄5常見錯誤及解決辦法。


簽名方法需要放在後臺,不放在vue前端,而是前端向後臺請求signature

簽名方法:
/* signature.js */
const config = require('../config')
const crypto = require('crypto')
const API = require('../wechat/api')
const api = new API(config.wechat.appid, config.wechat.appsecret)

// 生成隨機字符串
let createNonceStr = function () {
  return Math.random().toString(36).substr(2, 15);
}
// 生成時間戳
let createTimestamp = function () {
  return parseInt(new Date().getTime() / 1000) + '';
}
//對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)後,使用URL鍵值對的格式(即key1 = value1 & key2=value2…)拼接成字符串string
let raw = function (args) {
    let keys = Object.keys(args)
    keys = keys.sort()
    let string = ''
    keys.forEach(k => {
        string += '&' + k + '=' + args[k]
    })
    string = string.substr(1)
    return string
}
//生成簽名
class Signature {
  static async sign (ctx) {
    const { jsapi_ticket } = await api.ensureTicket() //獲取到的jsapi_ticket, 具體可看https://github.com/Laumlin/wechat-public
    let ret = {
      jsapi_ticket: jsapi_ticket,
      noncestr: createNonceStr(),
      timestamp: createTimestamp(),
      url: ctx.request.body.url
    }
    const string = raw(ret)
    let hash = crypto.createHash('sha1')
    hash.update(string)
    ret.signature = hash.digest('hex')
    ctx.response.body = ret
  }
}

module.exports = Signature

步驟四:通過ready接口處理成功驗證

wx.ready(function(){
    // config信息驗證後會執行ready方法,所有接口調用都必須在config接口獲得結果之後,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。
});
<script>
export default {
  mounted() {
    this.loadScript('http://res.wx.qq.com/open/js/jweixin-1.4.0.js', () => {
      console.log('import jssdk success')

      let url = `${NODE_URL}${this.$store.state.redirectURL}` // 獲取當前頁面的url

      this.$axios({
        method: 'post',
        url:`${MP_BASEURL}/getSignature`,
        data: {
          'url': url
        }
      }).then(res => {
        wx.config({
          debug: false, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
          appId: this.$store.state.wechatAppId, // 必填,公衆號的唯一標識
          timestamp: res.data.timestamp, // 必填,生成簽名的時間戳
          nonceStr: res.data.noncestr, // 必填,生成簽名的隨機串
          signature: res.data.signature,// 必填,簽名
          jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData','onMenuShareTimeline', 'onMenuShareAppMessage'] // 必填,需要使用的JS接口列表
        })
        wx.ready(()=>{
          this.setWechatConfig(this.config)
        })
      })
    })
  },
  computed: {
    config () {
      return {
          title: 'share_title',
          desc: 'share_description',
          img:'',
          img_title:'share_img_title',
          link: `${NODE_URL}${this.$route.fullPath}`
        }
    },
  },
  methods: {
    onComplete (way) {
      return () => {
        // 成功回調
      }
    },
    setWechatConfig (newVal) {
      // 自定義“分享給朋友”及“分享到QQ”按鈕的分享內容
      wx.onMenuShareAppMessage({
        title: newVal.title, // 分享標題
        desc: newVal.desc, // 分享描述
        link: newVal.link, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致
        imgUrl: newVal.img, // 分享圖標
        type: 'link',
        dataUrl: '',
        success: this.onComplete('wechatFriend')
      })
      // 自定義“分享到朋友圈”及“分享到QQ空間”按鈕的分享內容
      wx.onMenuShareTimeline({
        title: newVal.title, // 分享標題
        link: newVal.link, // 分享鏈接,該鏈接域名或路徑必須與當前頁面對應的公衆號JS安全域名一致
        imgUrl: newVal.img, // 分享圖標
        success: this.onComplete('wechatTimeline')
      })
    },
  }
}
 </script>

填坑:

url必須是當前網址的url,不包含‘#’後面部分,不能是本地地址http://localhost:8080/之類的,而且url的domain必須在微信安全域名之中。這也是我之前很頭痛的一件事,後臺返回的簽名和微信簽名檢工具一樣,任然報錯,一直報invalid signature的錯誤,找到半天,簽名和驗證工具簽名一致,只能說明你簽名時的url和wx校驗時的url不一致,原來用local域的url生成的簽名wx.config檢驗不通過,最後在線上環境,url的域名是可以直接訪問到的,簽名終於通過了。 還有一點需要注意的是url中如果有中文,只需要對中文作encodeURIComponent()處理,’/'等符號不用。
總結:微信公衆號和jssdk的開發都需要在線上環境,或者你在本地設置域名擊穿等設置,讓外網能直接訪問到你本地,最後吐槽一下:微信對開發者太不友好了~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章