[TOC]
## 3.4 微信登錄
微信登錄的整個過程,如圖4-22所示。

:-: 圖4-22 微信登錄的整個過程
### 3.4.1 獲取微信登錄憑證code
wx.login是生成一個帶有時效性的憑證,就像是一個會過期的臨時身份證一樣,在wx.login調用時,會先在微信后臺生成一張臨時的身份證,其有效時間僅為5分鐘 ` ??.hy[界面圖] `。然后把這個臨時身份證返回給小程序方,這個臨時的身份證我們把它稱為微信登錄憑證code。
由于這個臨時身份證5分鐘后會過期,如果黑客要冒充一個用戶的話,那他就必須在5分鐘內窮舉所有的身份證id,然后去開發者服務器換取真實的用戶身份。顯然,黑客要付出非常大的成本才能獲取到一個用戶信息,同時,開發者服務器也可以通過一些技術手段檢測到5分鐘內頻繁從某個ip發送過來的登錄請求,從而拒絕掉這些請求。
### 3.4.2 發送code到開發者服務器
在wx.login的success回調中拿到微信登錄憑證,緊接著會通過wx.request把code傳到開發者服務器,為了后續可以換取微信用戶身份id(注意id不是指微信用戶的微信號/密碼等隱私信息,可以理解為id是微信用戶在服務器的一個編號)。如果當前微信用戶還沒有綁定當前小程序業務的用戶身份,那在這次請求應該順便把用戶輸入的帳號密碼(是用戶在業務側的帳號密碼,而不是其微信的帳號密碼)一起傳到后臺,然后開發者服務器就可以校驗賬號密碼之后再和微信用戶id進行綁定,小程序端的示例代碼如下所示。
代碼清單4-12 wx.login獲取code后
~~~
Page({
tapLogin: function() {
wx.login({
success: function(res) {
if (res.code) {
wx.request({
url: 'https://test.com/login',
data: {
username: 'zhangsan', // 用戶輸入的賬號
password: 'pwd123456', // 用戶輸入的密碼
code: res.code
},
success: function(res) {
// 登錄成功
if (res.statusCode === 200) {
console.log(res.data.sessionId)// 服務器回包內容
}
}
})
} else {
console.log('獲取用戶登錄態失敗!' + res.errMsg)
}
}
});
}
})
~~~
### 3.4.3 到微信服務器換取微信用戶身份id
到了這一步,開發者的后臺就拿到了前邊wx.login()所生成的微信登錄憑證code,此時就可以拿這個code到微信服務器換取微信用戶身份。
微信服務器為了確保拿code過來換取身份信息的人就是剛剛對應的小程序開發者,到微信服務器的請求要同時帶上AppId和AppSecret,這兩個信息在小程序管理平臺的開發設置界面(小程序管理平臺地址`https://mp.weixin.qq.com`)可以看到。由此可以看出,AppId和AppSecret是微信鑒別開發者身份的重要信息:
`AppId`是公開信息,泄露AppId不會帶來安全風險,但是`AppSecret`是開發者的隱私數據不應該泄露,如果發現泄露需要到小程序管理平臺進行重置AppSecret,而code在成功換取一次信息之后也會立即失效,即便憑證code生成時間還沒過期。
開發者服務器和微信服務器通信也是通過HTTPS協議,微信服務器提供的接口地址是:
`https://api.weixin.qq.com/sns/jscode2session?appid=<AppId>&secret=<AppSecret>&js_code=<code>&grant_type=authorization_code`
URL的query部分的參數中 \<AppId>, \<AppSecret>, \<code> 就是前文所提到的三個信息,請求參數合法的話,接口會返回以下字段。
:-: 表4-3 jscode2session接口返回字段
| 字段 | 描述 |
| --- | --- |
| openid | 微信用戶的唯一標識 |
| session_key | 會話密鑰 |
| unionid | 用戶在微信開放平臺的唯一標識符。本字段在滿足一定條件的情況下才返回。|
`openid`就是前文一直提到的微信用戶id,可以用這個id來區分不同的微信用戶。
`session_key`則是微信服務器給開發者服務器頒發的身份憑證,開發者可以用session_key請求微信服務器其他接口來獲取一些其他信息,由此可以看到,session_key不應該泄露或者下發到小程序前端。
設計session_key的原因:如果我們每次都通過小程序前端wx.login()生成微信登錄憑證code去微信服務器請求信息,步驟太多造成整體耗時比較嚴重,因此對于一個比較可信的服務端,給開發者服務器頒發一個時效性更長的會話密鑰就顯得很有必要了。session_key也存在過期時間,具體內容可以參考小程序的官方文檔關于session_key的相關介紹。
### 3.4.4 綁定微信用戶身份id和業務用戶身份
在3.4.2節提到,業務側用戶還沒綁定微信側身份時,會讓用戶填寫業務側的用戶名密碼,這兩個值會和微信登錄憑證一起請求開發者服務器的登錄接口,此時開發者后臺通過校驗用戶名密碼就拿到了`業務側的用戶身份id`,通過code到微信服務器獲取`微信側的用戶身份openid`。微信會建議開發者把這兩個信息的對應關系存起來,我們把這個對應關系稱之為“綁定”。
有了這個綁定信息,小程序在下次需要用戶登錄的時候就可以不需要輸入賬號密碼,因為通過wx.login()獲取到code之后,可以拿到用戶的微信身份openid,通過綁定信息就可以查出業務側的用戶身份id,這樣靜默授權的登錄方式顯得非常便捷。
` ??.hy[這樣靜默授權的登錄方式是否有風險] `
### 3.4.5 業務登錄憑證SessionId
3.4.3節已經說到`微信側返回的session_key`是開發者服務器和微信服務器的會話密鑰,同樣道理,開發者服務器和開發者的小程序應該也有會話密鑰,在本書中我們就把它稱之為`SessionId`。
>[info] hy:使用http協議,因為http協議是無狀態協議,可以前端(browser)和后端(server)通過Session會話機制免去每次通信時的3次握手,從而提高通信效率。
> session_key中,前端:開發者服務器;后端:微信服務器
> SessionId中,前端:開發者的小程序;后端:開發者服務器
用戶登錄成功之后,開發者服務器需要生成會話密鑰SessionId,在服務端保持SessionId對應的用戶身份信息,同時把SessionId返回給小程序。小程序后續發起的請求中攜帶上SessionId,開發者服務器就可以通過服務器端的Session信息查詢到當前登錄用戶的身份,這樣我們就不需要每次都重新獲取code,省去了很多通信消耗。我們在4.6.4還會提到如何利用本地數據緩存的能力把SessionId存儲起來,以便在它還沒過期的時候能重復利用,以提高通信的性能。
- 微信
- 小程序
- 1. 代碼組成
- 1.1 JSON配置--'*.json'文件
- 1.2 WXML模板--'*.wxml'文件
- 1.3 WXSS樣式--'*.wxss'文件
- 1.4 JavaScript腳本--'*.js'文件
- 2. 客戶端運行
- 2.1 邏輯層和渲染層
- 2.1.1 邏輯層--App Service
- 2.1.2 渲染層/視圖層--View
- 2.1.3 通信模型
- 2.1.4 數據驅動
- 2.1.5 雙線程下的界面渲染
- 2.2 程序與頁面
- 2.3 組件
- 2.4 API
- 2.5 事件
- 2.6 兼容
- 3. 應用設計
- 3.1 Flex布局
- 3.2 界面常見的交互反饋
- 3.3 發起HTTPS網絡通信--wx.request
- 3.4 微信登錄
- 3.5 本地數據緩存
- 3.6 設備能力
- 4. 小程序的協同工作和發布
- 4.1 協同工作
- 4.2 用戶體驗審視
- 4.3 發布
- 4.4 運營
- 5. 底層框架
- 5.1 雙線程模型
- 5.2 組件系統--Exparser框架
- 5.3 原生組件
- 5.4 小程序與客戶端通信原理
- 6. 運行和性能優化
- 6.1 啟動--代碼加載
- 6.2 頁面準備
- 6.3 數據通信
- 6.4 視圖層渲染
- 6.5 原生組件通信
- 7. 小程序基礎庫的更新迭代
- 8. 微信開發者工具
- 騰訊云支持
- wafer
- Wafer2 快速開發 Demo - PHP
- WXAPI
- api列表