小程序檔案館 - 登錄授權(quán)
智能小程序運行在百度 App 之類的宿主上時婴程,相比普通的 H5 應(yīng)用有一項很大的優(yōu) 勢谊却,就是可以調(diào)用宿主應(yīng)用提供的各種 API 從而完成各種在 H5 實現(xiàn)比較困難的功 能扒袖,其中使用宿主的賬號體系完成登錄授權(quán)就是一項很重要的功能矮烹。
智能小程序在宿主應(yīng)用中進行登錄的方案是OAuth 2.0協(xié)議的變種,使用這種方案 進行登錄的優(yōu)勢主要有以下幾點:
某些小程序需要有一套用戶體系來識別用戶鞠柄,進而提供對應(yīng)的服務(wù)继控,但是往往 在用戶注冊這個環(huán)節(jié)上由于用戶覺得麻煩而放棄使用械馆,導(dǎo)致獲取到的用戶又流 失掉了。使用 OAuth 系的登錄方式武通,智能小程序可以直接使用用戶在百度 App 等宿主上的賬號霹崎,用戶登錄了宿主的賬號,就可以直接使用宿主賬號在小 程序里登錄冶忱。?
普通用戶要記住自己在不同智能小程序中的用戶名尾菇、郵箱、密碼是一個比較困 難的事情,許多用戶會選擇在大多數(shù)應(yīng)用中使用相同的用戶名派诬、密碼等信息劳淆, 而這又容易導(dǎo)致某個應(yīng)用的用戶數(shù)據(jù)泄露后入侵者可以拿這些數(shù)據(jù)去嘗試登錄 其它智能小程序,從而入侵其它原本安全的應(yīng)用默赂,這種即是常見的“撞庫”攻擊沛鸵。 使用 OAuth 系的登錄方式,一般應(yīng)用將用戶登錄的安全校驗托管給提供OAuth 服務(wù)的應(yīng)用缆八,可以避免被“撞庫”的風(fēng)險曲掰,提高應(yīng)用的安全性。
使用宿主賬號體系進行登錄
使用宿主的賬號體系進行登錄的全流程可見小程序開發(fā)文檔中的登錄章節(jié)奈辰,這里對使用此章節(jié)的內(nèi)容進行登錄做更詳細的介紹栏妖。
獲取 OpenID 和 SessionKey
在小程序的關(guān)聯(lián)登錄機制中,有兩個非常重要的概念:
OpenID 是宿主為每個用戶在每個小程序中生成的用戶標(biāo)識奖恰,一個相同的 OpenID 可以始終對應(yīng)同一個宿主賬號的用戶底哥。?
SessionKey 是宿主為每個用戶在每個小程序中生成的數(shù)據(jù)密鑰,數(shù)據(jù)密鑰有 兩個重要的作用:首先是通過宿主提供的接口獲取到的數(shù)據(jù)會使用 SessionKey 加密房官,可以保證開發(fā)者服務(wù)端拿到的數(shù)據(jù)是真實有效的,其次是 SessionKey 會有更新機制续滋,保證用戶登錄信息的安全翰守。后面會在登錄標(biāo)識章節(jié)再詳細討論。
小程序獲取用戶的 OpenID 和 SessionKey 的流程圖如下:
圖中接口的詳細參數(shù)可以參加開發(fā)者文檔中的登錄章節(jié)疲酌。
需要特別提到的一點是:swan.Login()不需要用戶授權(quán)蜡峰,也就是說在用戶已登錄 宿主賬號的情況下整個流程是完全靜默完成的,但是當(dāng)用戶在宿主應(yīng)用上并未登錄 的時候朗恳,調(diào)用此接口會彈出宿主應(yīng)用的登錄窗口湿颅,用戶需要完成登錄后再繼續(xù)進行。 如果需要在用戶未登錄的時候做其它邏輯處理而不是要求用戶登錄粥诫,可以先通過 swan.IsLoginSync()判斷用戶的登錄狀態(tài)油航。
登錄標(biāo)識
當(dāng)開發(fā)者獲取到用戶的 OpenID 和 SessionKey 之后,開發(fā)者應(yīng)該生成一個唯一的 登錄標(biāo)識返回給小程序怀浆,并寫到小程序的本地存儲中谊囚,在后續(xù)的請求開發(fā)者的服務(wù) 端接口時通過參數(shù)帶上這個唯一標(biāo)識,供后端通過這個登錄標(biāo)識找到對應(yīng)的 OpenID 和 SessionKey执赡。
一種比較好的登錄標(biāo)識的設(shè)計范例是:Hash(OpenID + SessionKey + Salt)镰踏,其中 Salt 是開發(fā)者自己定義的一個隨機字符串,Hash 可以考慮使用 SHA1 或者 MD5 等碰 撞概率非常小的哈希函數(shù)沙合。
一種非常不安全的方式是:直接使用 OpenID 或 SessionKey 的明文作為登錄標(biāo)識奠伪。 使用 OpenID 作為登錄標(biāo)識的風(fēng)險在于:同一個賬號的 OpenID 永遠不會變,因此 若攻擊者獲取到某個用戶的 OpenID,則可在參數(shù)中一直使用此 OpenID 來偽裝成此 用戶訪問開發(fā)者的服務(wù);使用 SessionKey 明文作為登錄標(biāo)識的風(fēng)險在于绊率,攻擊者 不光可以直接使用此 SessionKey 解密用戶數(shù)據(jù)谨敛,還可以使用它加密出假的用戶數(shù) 據(jù),然后通過開發(fā)者提供的服務(wù)端提口篡改用戶的數(shù)據(jù)即舌。
總結(jié)來說佣盒,登錄標(biāo)識需要滿足:唯一性、有時效性顽聂,并不能帶上敏感信息肥惭。
使用 SessionKey 處理用戶數(shù)據(jù)
當(dāng)獲取到 SessionKey 和 OpenID 后,開發(fā)者就可以獲取到有效的用戶數(shù)據(jù)了紊搪。獲 取用戶數(shù)據(jù)主要有 swan.getUserInfo()和 swan.getPhoneNumber()等接口蜜葱。下面
以 swan.getUserInfo()為例,講解如何使用 SessionKey 安全有效地處理用戶信息耀石。
swan.getUserInfo()接口返回的內(nèi)容可以分為明文和密文兩部分牵囤,明文部分是為 了可以更快地顯示在小程序的頁面上,主要包括用戶的頭像滞伟、昵稱等信息;密文部 分是 data 和 iv 兩個字段揭鳞。
以消息推送的場景為例,開發(fā)者可能需要在消息內(nèi)容里以“親愛的 XXX”為開頭梆奈,此 時會用到用戶的 OpenID 和真實的用戶名野崇。如果開發(fā)者提供一個接口,在通過 swan.getUserInfo()獲取到用戶信息后直接將用戶信息以明文的方式發(fā)到開發(fā)者 自己的服務(wù)端某個接口中保存起來亩钟,就會出現(xiàn)一個安全問題:攻擊者可以偽造用戶 的名稱等各種信息來訪問開發(fā)者的服務(wù)器寫入假的用戶數(shù)據(jù)乓梨。
安全的做法是:開發(fā)者在前端獲取到 swan.getUserInfo()后,將上一節(jié)中所說的 用戶登錄標(biāo)識從 LocalStorage 中取出來清酥,加上 data 和 iv扶镀,將這三個數(shù)據(jù)發(fā)到自己 的服務(wù)器,先通過登錄標(biāo)識將 SessionKey 取出來焰轻,然后用 SessionKey 和 iv 一起 使用 AES-192-CBC 算法對 data 進行解密(解密詳細的過程及代碼示例可參見用戶數(shù)據(jù)的簽名驗證和加解密)臭觉,通過判斷解密是否成功,以及解密出的 OpenID 是否 與登錄標(biāo)識所關(guān)聯(lián)的 OpenID 一致來判斷數(shù)據(jù)的真實性辱志,再保存到自己的數(shù)據(jù)庫中胧谈。
特別說明一點,調(diào)用 swan.getUserInfo()這類接口會彈出授權(quán)提示框讓用戶進行 授權(quán)荸频,若用戶拒絕授權(quán)菱肖,再次調(diào)用此接口不會重復(fù)彈出授權(quán)提示框。開發(fā)者此時可 引導(dǎo)用戶重新授權(quán)旭从,并調(diào)用 swan.openSetting()讓用戶重新授權(quán)稳强。
SessionKey 有效性判斷
由上兩節(jié)可知场仲,開發(fā)者每次獲取用戶信息的時候,需要同時判斷本地是否有用戶標(biāo) 識退疫,以及 SessionKey 是否有效:若沒有本地標(biāo)識渠缕,則無法找到對應(yīng)的 SessionKey; 而若 SessionKey 已經(jīng)無效,說明開發(fā)者數(shù)據(jù)庫中的用戶 SessionKey 無法對宿主 所提供的 data 和 iv 進行解密褒繁。
判斷 SessionKey 是否有效可以通過調(diào)用 swan.checkSession()來判斷亦鳞。如果為 false,則走上述獲取 SessionKey 的流程重新讓用戶使用宿主賬號在小程序內(nèi)完成 登錄;若為 true棒坏,則說明不用重新?lián)Q取 SessionKey燕差,可以直接獲取用戶信息。
需要注意的一點是坝冕,在提供多點登錄的宿主應(yīng)用(例如百度 App)中徒探,有可能同一 個賬號會在多臺不同的設(shè)備上同時登錄,此時有可能會出現(xiàn)此賬號在多臺設(shè)備上先 后都換取過 SessionKey喂窟,但是在此過程中 SessionKey 發(fā)生了變化测暗,則變化前已換 取過 SessionKey 的設(shè)備上,本地存儲中的登錄標(biāo)識已經(jīng)失效(找不到對應(yīng)的 SessionKey)磨澡,而調(diào)用 swan.checkSession()仍然會得到 true碗啄,此時開發(fā)者應(yīng)當(dāng)在 登錄標(biāo)識已失效的設(shè)備上處理此類異常,重新?lián)Q取 SessionKey 并生成新的登錄標(biāo)識稳摄。
未登錄時的降級方案
由于宿主應(yīng)用并不一定強制用戶登錄挫掏,因此用戶也有可能處于未登錄狀態(tài)。此時開 發(fā)者可能不希望通過調(diào)用 swan.login()強制用戶登錄秩命,而是希望直接使用用戶的 設(shè)備標(biāo)識來關(guān)聯(lián)用戶,存儲一些非敏感的數(shù)據(jù)褒傅。因此智能小程序還提供一個 SwanID 的標(biāo)識弃锐,可視作用戶的設(shè)備標(biāo)識。
SwanID 可以通過 swan.getSwanId()獲取(接口文檔)殿托。SwanID 具有以下特征:
用戶在同一臺設(shè)備上使用同一個開發(fā)者所開發(fā)的不同智能小程序霹菊,得到的是相同的 SwanID。?
用戶在同一臺設(shè)備上使用不同開發(fā)者所開發(fā)的不同智能小程序支竹,得到的SwanID 是不同的旋廷。
靈活地使用 SwanID 和 OpenID 進行搭配,可以做到許多特殊的功能礼搁,例如:
在未登錄的時候饶碘,將數(shù)據(jù)與用戶的 SwanID 關(guān)聯(lián),當(dāng)用戶登錄后馒吴,通過 SwanID 找到用戶登錄前的數(shù)據(jù)扎运,然后與 OpenID 關(guān)聯(lián)瑟曲,比如常見的場景例如電商類小 程序?qū)徫镘嚁?shù)據(jù)的同步。?
通過 SwanID 限定一個指定的 OpenID 只能在一臺設(shè)備上使用某個小程序豪治,即 當(dāng)同一個 OpenID 先后出現(xiàn)了兩個 SwanID 時洞拨,可以將前一個 SwanID 的登錄態(tài) 踢出,比較常見的有 IM 類的小程序负拟。
總結(jié)?
開發(fā)者可使用 OpenID+SessionKey 的方式使用宿主的用戶體系完成自己的業(yè) 務(wù)需求烦衣。?
安全地處理 OpenID 和 SessionKey,不直接暴露到前端掩浙,而是基于它們生成用 戶的登錄標(biāo)識花吟,保證登錄標(biāo)識只在有效期內(nèi)可用非常重要。?
當(dāng)用戶拒絕授權(quán)獲取用戶信息時涣脚,再次調(diào)用 swan.getUserInfo()方法無法重 新彈框讓用戶同意示辈,此時可通過 swan.openSetting()方法讓用戶重新授權(quán)。?
當(dāng)用戶未登錄時遣蚀,可使用 SwanID 作為降級方案矾麻,處理一些不太敏感的業(yè)務(wù)。
智能小可愛?發(fā)布于2018-10-25 17:54?