我們一般都是先獲取到微信的 unionid,然后再通過 unionid 去登錄自己的網站,就可以關聯到用戶在自己網站上的 user_id,但是在小程序登錄中,有時候可以獲取到 unionid,有時候獲取不到,在獲取不到 unionid 的情況下,用戶無法正常登錄網站。
UnionID機制說明:
如果開發者擁有多個移動應用、網站應用、和公眾帳號(包括小程序),可通過 unionid 來區分用戶的唯一性,因為只要是同一個微信開放平台帳號下的移動應用、網站應用和公眾帳號(包括小程序),用戶的 unionid 是唯一的。換句話說,同一用戶,對同一個微信開放平台下的不同應用,unionid 是相同的。
同一個微信開放平台下的相同主體的 App、公眾號、小程序,如果用戶已經關注公眾號,或者曾經登錄過App或公眾號,則用戶打開小程序時,開發者可以直接通過
wx.login 獲取到該用戶UnionID,無須用戶再次授權。(解讀:用戶如果沒有登錄過app,也沒有登錄過公眾號,也沒有關注過公眾號的情況下,小程序中通過 wx.login 是獲取不到 unionid的)
簡而言之,微信針對不同的用戶在不同的應用下都有唯一的一個 openId, 但是要想確定用戶是不是同一個用戶,就需要靠 unionid 來區分。
通常自己的后台都會有自己的一個用戶表,每個用戶有不同的 userid。也就是說同一個用戶在同一個微信開放平台下的相同主體的應用對應着相同的 userid, unionid 以及不同的 openid。所以在用戶登錄進來的時候,我們只能靠微信返回給我們的 unionid 去判斷是不是同一個用戶,再去關聯我們的用戶表,拿到對應的 user_id。
一般情況下(即在登錄小程序之前,已經關注過公眾號或已經登錄過公眾號或已經使用微信登錄的方式登錄過app),用戶通過以下兩步就正常成功登錄網站。
1 1. 獲取code(登錄憑證,用來換取openid及session_key等) 2 wx.login({ 3 success: function(res){ 4 if(res.code){ 5 that.getNeededUserInfo(res.code); 6 }else{ 7 console.log('獲取用戶登錄態失敗!'+res.errMsg); 8 } 9 } 10 }) 11 12 2. 獲取用戶信息(利用wx.login返回的code獲取用戶的信息) 13 getNeededUserInfo: function(code){ 14 wx.request({ 15 url: 'https://my.com/login', 16 method: 'POST', 17 data: { 18 code: code // 后端通過這個code去調用微信的接口(https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code),傳入參數code、appid、appsecret后獲取到微信返回的unionid、openid及session_key等。(然后后端可以直接利用微信返回的信息去關聯用戶在自己網站的user_id) 19 }, 20 success: function(res){ 21 // 可以返回前端需要的用戶信息(包括unionid、openid、user_id等) 22 } 23 }) 24 }
二般情況下(即在登錄小程序之前,既沒有關注過公眾號,也沒有登錄過公眾號,更沒有使用微信登錄的方式登錄過app),通過 wx.login 的到的 code 換不回 unionid 及 openid 等信息。
解決思路:通過帶登錄態的
wx.getUserInfo 獲取到用戶的加密數據 encryptedData 和加密算法的初始向量iv,然后將 encryptdata、iv 以及 code傳給后端,后端再去通過接收到的encryptedData、iv以、code 以及之前的 session_key 解密出用戶的 openid、unionid 等。
加密數據解密算法
以下是具體實現步驟:
1. 獲取code(登錄憑證,用來換取openid及session_key等) wx.login({ success: function(res){ if(res.code){ that.getNeededUserInfo(res.code); }else{ console.log('獲取用戶登錄態失敗!'+res.errMsg); } } }) 2. 獲取加密數據和加密算法初始向量 舊版本基礎庫調取wx.getUserInfo()可以直接獲取到微信返回的encryptdata等完整數據,基礎庫更新之后,需要增加withCredentials屬性,並將屬性值設置為true時才可以獲取到除用戶基本信息之外的encryptedData以及iv等數據。 需要注意的是:當withCredentials值為true時,要求此前有調用過wx.login且登錄態尚未過期。 getEncData: function(){ wx.getUserInfo({ withCredentials: true, success: function(res){ that.getNeededUserInfo( code, res.encryptedData, res.encryptedData ); } }) } 3. 獲取用戶信息(利用wx.login返回的code獲取用戶的信息) getNeededUserInfo: function(code, enc, iv){ wx.request({ url: 'https://my.com/login', method: 'POST', data: { code: code, encryptedData: enc, iv: iv }, success: function(res){ // 可以返回前端需要的用戶信息(包括unionid、openid、user_id等) } }) }
實際項目中需要將以上兩種情況整合以后使用。
思路有兩種:
第一種:( 前端判斷是否有 unionid )在向后端上傳 code 並且后端返回數據以后,前端判斷返回值中是否有 unionid 或者 unionid 是否為 null,null 的情況下去調用帶有用戶登錄態的wx.getUserInfo(),然后再將微信返回的 encryptedData 和 iv 返回給后端,后端解密出相應的信息后再返回給前端;
第二種:( 后端判斷是否有 unionid )前端在調用 wx.getUserInfo() 時候帶着登錄態,然后不管后台能不能拿到 unionid,都把 encryptedData 和 iv 返回給后端,后端在拿到前端 code 之后去請求微信的接口拿 unionid,如果返回的 unionid 為空,再拿前端傳的 encryptedData、iv以及之前的 session_key 解密出 unionid。