小程式 登入(轉)
阿新 • • 發佈:2019-01-11
轉自:https://segmentfault.com/a/1190000012731720
梳理登入流程
這裡有幾個點是要注意的:
- 要注意 es6語法使用,es6的語法會在小程式裡面更加的有用,其中最關鍵的地方就是小程式的 api 大部分都是非同步的,舊有的方式要非同步就必須要要回調,而回調的就會導致程式碼邏輯容易發生混亂,所以需要使用promise並且進行介面的封裝。
- 要注意效率,例如對於 code 的獲取,首次和非首次要注意首次獲取後儲存下來,然後非首次獲取就可以從快取裡面直接獲取,類似這種有好幾個地方,所以需要注意好。
- 要注意先理清楚業務邏輯和登入邏輯和產品體驗邏輯,一定要先畫好流程圖,因為小程式的邏輯相較一般的邏輯要稍微“繞”,對,是很繞的繞,所以需要注意理順流暢。
- 要注意介面的呼叫先後順序,跟流程圖的關係很大,對比官方文件,一步步處理,又因為使用非同步,所以需要弄清楚 promise 非同步的 resolve 和 reject,在多重promise 裡面會比較麻煩,不過也是有技巧可以迴避的,詳情下面會說。
- 需要注意簽名解密處理,雖然這是伺服器端做的,但是也需要了解好是怎麼一個操作,因為也需要解析給伺服器端的同事,如何配合小程式來做處理。
- 需要理解各個關鍵的變數元素的意思,code,session_key,3rd_session,openid,unioinid的意義,這樣才能協助理解文件的整體思路。
- 需要注意 openId 和 unioinId的使用
微信小程式登入流程圖
引用Yinjie 的圖,因為這個圖比官方的要看得明白一點。
程式碼邏輯流程圖
引用Yinjie 的圖,因為這個圖比官方的要看得明白一點。
騰訊 weapp-session的程式碼流程圖
他們還出了一個比較詳細的,一步步的程式碼處理流程,可以對比自己的程式進行檢查。
- 客戶端(微信小程式)發起請求
request
首次請求
- 呼叫
wx.login()
和wx.getUserInfo()
介面獲得code
、rawData
和signature
requset
的頭部帶上code
、rawData
和signature
- 儲存
code
供下次呼叫
- 呼叫
非首次請求
request
的頭部帶上儲存的code
伺服器收到請求
request
code
、rawData
和signature
欄位- 如果
code
為空,跳到第4
步 如果
code
不為空,且rawData
不為空,需要進行簽名校驗使用
code
,appid
、app_secret
請求微信介面獲得session_key
和openid
- 如果介面失敗,響應
ERR_SESSION_KEY_EXCHANGE_FAILED
- 如果介面失敗,響應
- 使用簽名演算法通過
rawData
和session_key
計算簽名signature2
對比
signature
和signature2
簽名一致,解析
rawData
為wxUserInfo
- 把
openid
寫入到wxUserInfo
- 把
(code, wxUserInfo)
快取到 Redis - 把
wxUserInfo
存放在request.$wxUserInfo
裡 - 跳到第
4
步
- 把
- 簽名不一致,響應
ERR_UNTRUSTED_RAW_DATA
如果
code
不為空,但rawData
為空,從 Redis 根據code
查詢快取的使用者資訊- 找到使用者資訊,存放在
request.$wxUserInfo
欄位裡,跳到第4
步 - 沒找到使用者資訊(可能是過期),響應
ERR_SESSION_EXPIRED
- 找到使用者資訊,存放在
- 如果
request
被業務處理,可以使用request.$wxUserInfo
來獲取使用者資訊(request.$wxUserInfo
可能為空,業務需要自行處理)
流程圖總結
code
是微信使用者的登入憑證,開啟小程式登入的時候獲取的只屬於微信這個使用者的登入憑證,需要注意的是,這個登入憑證只供微信小程式使用的。session_key
是微信使用者在小程式裡面的登入態資訊,相當於是微信給這個使用者頒發的一個登入 session,官網地址- 他有一個過期時間
{"session_key":"...","expires_in":7200,"openid":"..."}
,需要定期使用wx.checkSession檢測。
- 他有一個過期時間
openId
,使用者的唯一標識unioinId
,如果開發者擁有多個移動應用、網站應用、和公眾帳號(包括小程式),可通過unionid
來區分使用者的唯一性,因為只要是同一個微信開放平臺帳號下的移動應用、網站應用和公眾帳號(包括小程式),使用者的unionid
是唯一的。換句話說,同一使用者,對同一個微信開放平臺下的不同應用,unionid
是相同的。- 一般來說,
openId
就是微信使用者的唯一標識,但是因為微信產品很多,所以會出現多個微信產品使用不同的openId
來標識使用者,但是對於我們做業務接入的話,就買辦法使用了,所以建議是統一使用unioinid
,因為一般來說,一般的業務都會有公眾號,小程式聯合使用的。 3rd_session
是一般是指我們自己公司的伺服器的session
,一般來說,可以跟原來的業務的session
一起使用,不過這個session
的過期時間一定要比小程式的session_key
的過期時間要長,這樣可以減少session
的多次重複建立,另外一般我們自己公司的伺服器的session
管理都會使用類似 redis 之類的資料庫進行管理的,這個大致瞭解一下,因為其他文章會提到。- 為什麼要用2個
session
(session_key
和3rd_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個地方:
- 需要返回一個可使用的 promise 例項
- 即使有非同步並且出現分支的情況下,儘量集中在一個統一的流裡面處理,用統一的 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) {
}
});
}
}
})
}
}
});