小程序簡單開發(一)-關於登陸的一些事

小程序簡單開發(一)-關於登陸的一些事

梳理登陸流程

這裏有幾個點是要注意的:html

  1. 要注意 es6語法使用,es6的語法會在小程序裏面更加的有用,其中最關鍵的地方就是小程序的 api 大部分都是異步的,舊有的方式要異步就必需要要回調,而回調的就會致使代碼邏輯容易發生混亂,因此須要使用promise而且進行接口的封裝。
  2. 要注意效率,例如對於 code 的獲取,首次和非首次要注意首次獲取後保存下來,而後非首次獲取就能夠從緩存裏面直接獲取,相似這種有好幾個地方,因此須要注意好。
  3. 要注意先理清楚業務邏輯和登陸邏輯和產品體驗邏輯,必定要先畫好流程圖,由於小程序的邏輯相較通常的邏輯要稍微「繞」,對,是很繞的繞,因此須要注意理順流暢。
  4. 要注意接口的調用前後順序,跟流程圖的關係很大,對比官方文檔,一步步處理,又由於使用異步,因此須要弄清楚 promise 異步的 resolve 和 reject,在多重promise 裏面會比較麻煩,不過也是有技巧能夠迴避的,詳情下面會說。
  5. 須要注意簽名解密處理,雖然這是服務器端作的,可是也須要了解好是怎麼一個操做,由於也須要解析給服務器端的同事,如何配合小程序來作處理。
  6. 須要理解各個關鍵的變量元素的意思,code,session_key,3rd_session,openid,unioinid的意義,這樣才能協助理解文檔的總體思路。
  7. 須要注意 openId 和 unioinId的使用

微信小程序登陸流程圖

引用Yinjie 的圖,由於這個圖比官方的要看得明白一點。git

clipboard.png

代碼邏輯流程圖

引用Yinjie 的圖,由於這個圖比官方的要看得明白一點。es6

clipboard.png

騰訊 weapp-session的代碼流程圖

引用騰訊 weapp-session 的圖github

clipboard.png

他們還出了一個比較詳細的,一步步的代碼處理流程,能夠對比本身的程序進行檢查。
  1. 客戶端(微信小程序)發起請求 request
  2. weapp-session-client 包裝 requestredis

    • 首次請求算法

      • 調用 wx.login()wx.getUserInfo() 接口得到 coderawDatasignature
      • requset 的頭部帶上 coderawDatasignature
      • 保存 code 供下次調用
    • 非首次請求數據庫

      • request 的頭部帶上保存的 code
  3. 服務器收到請求 request,中間件從頭部提取 coderawDatasignature 字段小程序

    • 若是 code 爲空,跳到第 4
    • 若是 code 不爲空,且 rawData 不爲空,須要進行簽名校驗微信小程序

      • 使用 codeappidapp_secret 請求微信接口得到 session_keyopenidapi

        • 若是接口失敗,響應 ERR_SESSION_KEY_EXCHANGE_FAILED
      • 使用簽名算法經過 rawDatasession_key 計算簽名 signature2
      • 對比 signaturesignature2

        • 簽名一致,解析 rawDatawxUserInfo

          • openid 寫入到 wxUserInfo
          • (code, wxUserInfo) 緩存到 Redis
          • wxUserInfo 存放在 request.$wxUserInfo
          • 跳到第 4
        • 簽名不一致,響應 ERR_UNTRUSTED_RAW_DATA
    • 若是 code 不爲空,但 rawData 爲空,從 Redis 根據 code 查詢緩存的用戶信息

      • 找到用戶信息,存放在 request.$wxUserInfo 字段裏,跳到第 4
      • 沒找到用戶信息(多是過時),響應 ERR_SESSION_EXPIRED
  4. request 被業務處理,可使用 request.$wxUserInfo 來獲取用戶信息(request.$wxUserInfo 可能爲空,業務須要自行處理)

流程圖總結

  1. code 是微信用戶的登陸憑證,打開小程序登陸的時候獲取的只屬於微信這個用戶的登陸憑證,須要注意的是,這個登陸憑證只供微信小程序使用的。
  2. session_key 是微信用戶在小程序裏面的登陸態信息,至關因而微信給這個用戶頒發的一個登陸 session,官網地址

    • 他有一個過時時間{"session_key":"...","expires_in":7200,"openid":"..."},須要按期使用wx.checkSession檢測。
  3. openId ,用戶的惟一標識
  4. unioinId,若是開發者擁有多個移動應用、網站應用、和公衆賬號(包括小程序),可經過unionid來區分用戶的惟一性,由於只要是同一個微信開放平臺賬號下的移動應用、網站應用和公衆賬號(包括小程序),用戶的unionid是惟一的。換句話說,同一用戶,對同一個微信開放平臺下的不一樣應用,unionid是相同的。
  5. 通常來講,openId 就是微信用戶的惟一標識,可是由於微信產品不少,因此會出現多個微信產品使用不一樣的 openId 來標識用戶,可是對於咱們作業務接入的話,就買辦法使用了,因此建議是統一使用 unioinid,由於通常來講,通常的業務都會有公衆號,小程序聯合使用的。
  6. 3rd_session 是通常是指咱們本身公司的服務器的 session,通常來講,能夠跟原來的業務的 session 一塊兒使用,不過這個 session 的過時時間必定要比小程序的session_key 的過時時間要長,這樣能夠減小 session 的屢次重複建立,另一般咱們本身公司的服務器的 session 管理都會使用相似 redis 之類的數據庫進行管理的,這個大體瞭解一下,由於其餘文章會提到。
  7. 爲何要用2個 sessionsession_key3rd_session),那是由於session_key是微信的登陸態,3rd_session是咱們業務系統的登陸態,兩邊各有一個登陸態,因此須要將2個登陸態合併爲一個 session,在這裏面是合併爲3rd_session,並保存到咱們業務系統上,而後每次須要使用的時候,小程序帶上這個3rd_session訪問咱們的業務系統,經過處理,能夠返回咱們須要 unioinid 和其餘 session 信息而不用每次都去獲取一個新的session_key(由於微信有限制code 的使用,要用 code 換 session_key),總的來講,就是使用3rd_session來管理小程序的登陸態

關於解密

根據官方文檔: ,數據校驗是爲了提升數據的安全性,咱們須要獲取用戶的 unioinid,須要調用wx.getUserInfo接口來獲取,可是通常狀況下,獲取出來的數據是

{
  "nickName": "Band",
  "gender": 1,
  "language": "zh_CN",
  "city": "Guangzhou",
  "province": "Guangdong",
  "country": "CN",
  "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"
}

對於須要業務接入的話,沒有 unioinid 是沒意義的,因此須要根據官方的方式來進行解密,解密後的數據裏面有 unioinid 了

{
    "openId": "OPENID",
    "nickName": "NICKNAME",
    "gender": GENDER,
    "city": "CITY",
    "province": "PROVINCE",
    "country": "COUNTRY",
    "avatarUrl": "AVATARURL",
    "unionId": "UNIONID",
    "watermark":
    {
        "appid":"APPID",
    "timestamp":TIMESTAMP
    }
}

封裝網絡接口

由於小程序的全部網絡請求都是異步的,那麼異步就會出現不少重的回調的問題,因此改爲了 promise,promise 的使用要謹慎注意 resolve 和 return的處理。

例如這樣:

const httpRequest = (api, data) => {
    let serverHost = env.serverHost;

    return new Promise(function (resolve, reject) {
        //發起網絡請求
        wx.request({
            url: serverHost + api,
            data: data,
            header: {
                'content-type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
                if (res.data.errno === 0) {
                    // 須要下一步處理的就用 resolve 返回
                    resolve(res.data);
                }
                else {
                    console.log("http fail:api:" + api + "res:" + JSON.stringify(res));
                    // 須要跳出循環處理的就用 reject
                    reject(res.data);
                }
            }, fail: function (res) {
                console.log("http fail:api:" + api + "res:" + JSON.stringify(res));
                // 須要跳出循環處理的就用 reject
                reject(res);
            }
        })
    })
};

在多重 promise 的狀況下,則須要注意2個地方:

  1. 須要返回一個可以使用的 promise 實例
  2. 即便有異步而且出現分支的狀況下,儘可能集中在一個統一的流裏面處理,用統一的 reject 跳出,而不是各個異步單獨跳出,這樣流程會更加統一和方便管理。
getCode() // 獲取 wx code
            .then(code => {
                wxCode = code;
                // 這裏getSetting是一個返回的 promise實例,如上面的那個
                return getSetting(); // 獲取 setting
            })
            .then(res => {
                if (res.authSetting["scope.userInfo"]) {
                    // 這裏getUserInfo是一個返回的 promise實例
                    return getUserInfo(self);
                } else {
                    console.log("first auth none:" + JSON.stringify(res));
                    // 相似
                    return util.showModal("親,尚未受權!")
                        .then(res => {
                            return getAuth("userInfo");
                        }).then(res => {
                            // 檢查受權是否正常
                            return getSetting();
                        })
                        .then(res => {
                            if (res.authSetting["scope.userInfo"]) {
                                return getUserInfo(self);
                            } else {
                                return Promise.reject(res);
                            }
                        });
                }
            })
            .catch(err=>{
                console.log("err"+err);    
            })

注意友好的提示

微信小程序「受權失敗」場景須要優雅處理,提高用戶體驗,參考這裏:能夠稍微看到是如何生效的:

經過在wx.getSetting裏面插入一個判斷處理,判斷沒有權限即彈出 modal 提示框:

wx.getSetting({
    success: function success(res) {
        console.log(res.authSetting);
        var authSetting = res.authSetting;
        if (authSetting['scope.userInfo'] === false) {
                wx.showModal({
                    title: '用戶未受權',
                    content: '如需XXXXXXX。',
                    showCancel: false,
                    success: function (res) {
                        if (res.confirm) {
                            console.log('用戶點擊肯定')
                            wx.openSetting({
                                success: function success(res) {
                                }
                            });
                        }
                    }
                })
        }
    }
});

參考引用:

  • 鳴謝里面提到的全部引用來源的做者。
相關文章
相關標籤/搜索