小程序掃碼登錄網頁端原理

 

作者:雨飛

一、問題引入

在設計用於管理社團信息的網頁端時,我們需要解決的一個問題是怎樣讓社團管理員很方便地登錄到管理平臺中去。出於提高用戶體驗角度的考慮,我們的小程序在用戶第一次登錄時,只需要微信授權獲得身份信息,就可以自動完成註冊和登錄流程,用戶無需手動輸入密碼或創建新的賬號,一切都是與微信綁定的。這種做法固然方便,但缺點在於登錄流程和微信環境高度綁定,脫離微信環境之後再想獲取並驗證用戶的身份信息就比較困難。

最後我們選擇了在網頁端設置小程序掃碼登錄,用戶只需要打開我們的小程序,在“我的”頁面中點擊“掃描二維碼”,便可以自動登錄到網頁端社團數據管理平臺。在實現這個功能的過程中,我們遇到了一些問題,這些問題在經歷了一段時間的學習研究之後得以解決,我們覺得有必要在此把解決問題的過程記錄下來,作爲技術博客。因此有了這篇文章。

二、幾個難題

1. 網頁端是怎麼知道哪個用戶掃描的二維碼?

這是很多人在第一次看到”掃碼登錄“這種登錄方式時的第一反應。的確,這個過程看起來有點神奇,網頁上只是顯示了一個二維碼而已,爲什麼小程序掃碼之後,網頁端就知道是哪個用戶掃描的二維碼呢?如果我們要自己去實現這個功能的話,也必然面臨着同樣的問題。

2. 小程序掃碼,掃出來的是什麼東西?

第二個問題實際上是由第一個問題引出的。既然網頁端不需要任何額外信息就能知道是哪個用戶掃描的二維碼,那麼二維碼裏就必然包含着對識別用戶身份至關重要的信息。這裏面的信息具體有哪些內容?這也是我們着重要解決的事情。

3. 小程序掃到二維碼以後,做了什麼事情,怎麼和網頁端通訊的?

從用戶體驗的角度來看,小程序掃碼登錄是一件十分自然的事情:網頁端顯示二維碼,小程序掃碼,網頁端便可以正常登錄。然而小程序掃碼之後,網頁端怎麼知道小程序已經掃碼了?小程序是如何把自身存儲的用戶信息發送給網頁端的?只有解決這些問題,掃碼登錄的流程纔可以說是被真正打通。

三、解決方案

1. 至關重要的uuid

uuid(通用唯一識別碼)是解決以上難題的關鍵。其實掃碼登錄的根本難題在於如何在小程序和網頁端之間共享用戶的身份信息,因此勢必需要一個識別碼來進行網頁端和小程序的通信綁定。

後端服務器對外提供一個接口,網頁端每次訪問這個接口,都會獲得一個全局唯一的uuid。網頁端獲取uuid之後,將其轉換爲二維碼展示在頁面上,二維碼包含且只包含這個uuid。

我們使用了qrcode.react作爲網頁端生成二維碼的組件。

// GetWxQrCode 獲取掃描二維碼
func (srv *UsersSrv) GetWxQrCode() (string, string, error) {
    appId := util.GetString("Wechat.app_id")
    appSecret := util.GetString("Wechat.app_secret")
    // pageUrl := util.GetString("Wechat.page_url")
    accessToken, err := srv.GetAccessToken(appId, appSecret)
    fmt.Sprintf(accessToken)
    var requestUrl = fmt.Sprintf(common.QrCodeUrl, accessToken)
    util.Info(requestUrl, "/users/GetWxQrCode")
    scene := strings.Replace(uuid.New().String(), "-", "", -1)
    // "{\"scene\":\""+scene+"\",\"page\":\""+pageUrl+"\"}"
    response, err := http.Post(requestUrl, "application/json", strings.NewReader(""+
        "{\"scene\":\""+scene+"\"}"))
    util.Info(fmt.Sprintf("", response), "/users/GetWxQrCode")
    if err != nil {
        return "", "", err
    }
    defer response.Body.Close() //在回覆後必須關閉回覆的主體
    body, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Println(err)
        return "", "", err
    }
    encodeStr := base64.StdEncoding.EncodeToString(body)
    var stateInfo models.StateInfo
    stateInfo.State = common.ToBeScanned
    data, err := json.MarshalIndent(stateInfo, "", "   ")
    if err != nil {
        return "", "", err
    }
    common.Set(scene, string(data), 60*60)
    return encodeStr, scene, nil
}

 

2. 用戶信息與uuid的綁定

後端服務器生成uuid之後,便會將這個uuid存入redis緩存中。小程序掃描到帶有uuid信息的二維碼,獲得用戶身份信息之後,訪問服務器的另一個接口,將用戶信息和uuid一起發送給後端。後端把身份信息與剛剛存入redis的uuid綁定在一起,由此完成用戶信息和uuid(也即二維碼)的綁定。

3. 網頁端輪詢uuid,直至獲取到用戶身份信息

網頁端知道,在未來的某一時刻,小程序會把當前登錄用戶的身份信息和uuid綁定在一起,因此網頁端只需要不斷輪詢後端服務器,並把自己的uuid作爲參數,就可以知道uuid是否已經綁定了用戶信息。如果還未綁定,則等待直至uuid失效;如果已綁定,就使用該身份信息登錄。至此,掃碼登錄的流程全部打通。

四、一些技術細節

  1. redis在存儲uuid時,出於安全考慮,必須設置過期時間
  2. 網頁端輪詢時,需要注意輪詢時間間隔,避免對服務器造成過大壓力
  3. uuid一旦與用戶身份信息綁定,一次使用後則需失效,否則會帶來安全隱患

轉自  https://www.cnblogs.com/buaareadsun/p/10886866.html

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