手把手教會你小程序登陸鑑權

導語

爲了方便小程序應用使用微信登陸態進行受權登陸,微信小程序提供了登陸受權的開放接口。乍一看文檔,感受文檔上講的很是有道理,可是實現起來又真的是摸不着頭腦,不知道如何管理和維護登陸態。本文就來手把手的教會你們在業務裏如何接入和維護微信登陸態javascript

接入流程

這裏官方文檔上的流程圖已經足夠清晰,咱們直接就該圖展開詳述和補充。html

img

首先你們看到這張圖,確定會注意到小程序進行通訊交互的不止是小程序前端和咱們本身的服務端,微信第三方服務端也參與其中,那麼微信服務端在其中扮演着怎樣的角色呢?咱們一塊兒來串一遍登陸鑑權的流程就明白了。前端

1. 調用wx.login生成code

wx.login()這個API的做用就是爲當前用戶生成一個臨時的登陸憑證,這個臨時登陸憑證的有效期只有五分鐘。咱們拿到這個登陸憑證後就能夠進行下一步操做:獲取openidsession_keyjava

wx.login({
    success: function(loginRes) {
        if (loginRes.code) {
            // example: 081LXytJ1Xq1Y40sg3uJ1FWntJ1LXyth
        }
    }
});

2. 獲取openid和session_key

咱們先來介紹下openid,用過公衆號的童鞋應該對這個標識都不陌生了,在公衆平臺裏,用來標識每一個用戶在訂閱號、服務號、小程序這三種不一樣應用的惟一標識,也就是說每一個用戶在每一個應用的openid都是不一致的,因此在小程序裏,咱們能夠用openid來標識用戶的惟一性。mysql

那麼session_key是用來幹嗎的呢?有了用戶標識,咱們就須要讓該用戶進行登陸,那麼session_key就保證了當前用戶進行會話操做的有效性,這個session_key是微信服務端給咱們派發的。也就是說,咱們能夠用這個標識來間接地維護咱們小程序用戶的登陸態,那麼這個session_key是怎麼拿到的呢?咱們須要在本身的服務端請求微信提供的第三方接口https://api.weixin.qq.com/sns/jscode2session,這個接口須要帶上四個參數字段:git

參數
appid 小程序的appid
secret 小程序的secret
js_code 前面調用wx.login派發的code
grant_type 'authorization_code'

從這幾個參數,咱們能夠看出,要請求這個接口必須先調用wx.login()來獲取到用戶當前會話的code。那麼爲何咱們要在服務端來請求這個接口呢?實際上是出於安全性的考量,若是咱們在前端經過request調用此接口,就不可避免的須要將咱們小程序的appid和小程序的secret暴露在外部,同時也將微信服務端下發的session_key暴露給「有心之人」,這就給咱們的業務安全帶來極大的風險。除了須要在服務端進行session_key的獲取,咱們還須要注意兩點:github

  • session_key和微信派發的code是一一對應的,同一code只能換取一次session_key。每次調用wx.login(),都會下發一個新的code和對應的session_key,爲了保證用戶體驗和登陸態的有效性,開發者須要清楚用戶須要從新登陸時纔去調用wx.login()
  • session_key是有失效性的,即使是不調用wx.login,session_key也會過時,過時時間跟用戶使用小程序的頻率成正相關,但具體的時間長短開發者和用戶都是獲取不到的
function getSessionKey (code, appid, appSecret) {
    var opt = {
        method: 'GET',
        url: 'https://api.weixin.qq.com/sns/jscode2session',
        params: {
            appid: appid,
            secret: appSecret,
            js_code: code,
            grant_type: 'authorization_code'
        }
    };
    return http(opt).then(function (response) {
        var data = response.data;
        if (!data.openid || !data.session_key || data.errcode) {
            return {
                result: -2,
                errmsg: data.errmsg || '返回數據字段不完整'
            }
        } else {
            return data
        }
    });
}

3. 生成3rd_session

前面說過經過session_key來「間接」地維護登陸態,所謂間接,也就是咱們須要本身維護用戶的登陸態信息,這裏也是考慮到安全性因素,若是直接使用微信服務端派發的session_key來做爲業務方的登陸態使用,會被「有心之人」用來獲取用戶的敏感信息,好比wx.getUserInfo()這個接口呢,就須要session_key來配合解密微信用戶的敏感信息。算法

那麼咱們若是生成本身的登陸態標識呢,這裏可使用幾種常見的不可逆的哈希算法,好比md五、sha1等,將生成後的登陸態標識(這裏咱們統稱爲'skey')返回給前端,並在前端維護這份登陸態標識(通常是存入storage)。而在服務端呢,咱們會把生成的skey存在用戶對應的數據表中,前端經過傳遞skey來存取用戶的信息。sql

能夠看到這裏咱們使用了sha1算法來生成了一個skey:shell

const crypto = require('crypto');

return getSessionKey(code, appid, secret)
    .then(resData => {
        // 選擇加密算法生成本身的登陸態標識
        const { session_key } = resData;
        const skey = encryptSha1(session_key);
    });
    
function encryptSha1(data) {
    return crypto.createHash('sha1').update(data, 'utf8').digest('hex')
}

4. checkSession

前面咱們將skey存入前端的storage裏,每次進行用戶數據請求時會帶上skey,那麼若是此時session_key過時呢?因此咱們須要調用到wx.checkSession()這個API來校驗當前session_key是否已通過期,這個API並不須要傳入任何有關session_key的信息參數,而是微信小程序本身去調本身的服務來查詢用戶最近一次生成的session_key是否過時。若是當前session_key過時,就讓用戶來從新登陸,更新session_key,並將最新的skey存入用戶數據表中。

checkSession這個步驟呢,咱們通常是放在小程序啓動時就校驗登陸態的邏輯處,這裏貼個校驗登陸態的流程圖:

img2

下面代碼即校驗登陸態的簡單流程:

let loginFlag = wx.getStorageSync('skey');
if (loginFlag) {
    // 檢查 session_key 是否過時
    wx.checkSession({
        // session_key 有效(未過時)
        success: function() {
            // 業務邏輯處理
        },
    
        // session_key 過時
        fail: function() {
            // session_key過時,從新登陸
            doLogin();
        }
    });
) else {
    // 無skey,做爲首次登陸
    doLogin();
}

5. 支持emoji表情存儲

若是須要將用戶微信名存入數據表中,那麼就確認數據表及數據列的編碼格式。由於用戶微信名可能會包含emoji圖標,而經常使用的UTF8編碼只支持1-3個字節,emoji圖標恰好是4個字節的編碼進行存儲。

這裏有兩種方式(以mysql爲例):

1.設置存儲字符集

在mysql5.5.3版本後,支持將數據庫及數據表和數據列的字符集設置爲utf8mb4,所以可在/etc/my.cnf設置默認字符集編碼及服務端編碼格式

// my.cnf

[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-client-handshake = FALSE
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

設置完默認字符集編碼及服務端字符集編碼,若是是對已經存在的表和字段進行編碼轉換,須要執行下面幾個步驟:

  • 設置數據庫字符集爲utf8mb4
ALTER DATABASE 數據庫名稱 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
  • 設置數據表字符集爲utf8mb4
ALTER TABLE 數據表名稱 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  • 設置數據列字段字符集爲utf8mb4
ALTER TABLE 數據表名稱 CHANGE 字段列名稱 VARCHAR(n) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

這裏的COLLATE指的是排序字符集,也就是用來對存儲的字符進行排序和比較的,utf8mb4經常使用的collation有兩種:utf8mb4_unicode_ciutf8mb4_general_ci,通常建議使用utf8mb4_unicode_ci,由於它是基於標準的Unicode Collation Algorithm(UCA)來排序的,能夠在各類語言進行精確排序。這兩種排序方式的具體區別能夠參考:What's the difference between utf8_general_ci and utf8_unicode_ci

2.經過使用sequelize對emoji字符進行編碼入庫,使用時再進行解碼

這裏是sequelize的配置,可參考Sequelize文檔

{
       dialect: 'mysql',    // 數據庫類型
       dialectOptions: {    
         charset: 'utf8mb4',
         collate: "utf8mb4_unicode_ci"
      },
}

最後

前面講了微信小程序如何接入微信登陸態標識的詳細流程,那麼如何獲取小程序中的用戶數據以及對用戶敏感數據進行解密,並保證用戶數據的完整性,我將在下一篇文章給你們作一個詳細地介紹。

騰訊IVWEB團隊的工程化解決方案feflow已經開源:Github主頁: feflow

若是對您的團隊或者項目有幫助,請給個Star支持一下哈~

相關文章
相關標籤/搜索