2021年2月24日,微信官方團隊釋出了一個調整通知:《小程式登入、使用者資訊相關介面調整說明》,公告明確從4月13日起,所有釋出的小程式將無法使用 wx.getUserInfo 介面(JS)和 <button open-type="getUserInfo"/> 標籤來獲取使用者資訊了。主要資訊如下:
實際時間從1個月前(4月2日)起,我們已經陸續接到開發者的反饋,在開發環境已經無法正常使用舊版本的功能,這也意味著從現在開始,要進行小程式的開發必須符合調整後介面的標準。
雖然文件看上去很負責,經過實際測試,其實修改的地方還是比較簡單的,步驟如下:
第一步:替換原有的 <button open-type="getUserInfo"/> 標籤為普通標籤,例如:
<button bindtap="getUserInfo"> 獲取頭像暱稱 </button>
在頁面的 .js 檔案中建立一個對應的方法 getUserInfo(如果以前就有可以直接修改):
getUserInfo: function (e) { //... }
第二步:在 getUserInfo 程式碼中呼叫 wx.getUserProfile 介面:
getUserProfile(e) { // 推薦使用wx.getUserProfile獲取使用者資訊,開發者每次通過該介面獲取使用者個人資訊均需使用者確認 // 開發者妥善保管使用者快速填寫的頭像暱稱,避免重複彈窗 wx.getUserProfile({ desc: '用於完善會員資料', // 宣告獲取使用者個人資訊後的用途,後續會展示在彈窗中,請謹慎填寫 success: (res) => { this.setData({ userInfo: res.userInfo, hasUserInfo: true }) } }) }
完成。
以下是新介面呼叫的效果:
未登入狀態 | 授權 | 完成授權 |
最新的 Demo 已經更新至 Senparc.Weixin SDK 的開源專案庫:https://github.com/JeffreySu/WeiXinMPSDK
小程式檔案目錄:\src\Senparc.Weixin.WxOpen\src\Senparc.Weixin.WxOpen.AppDemo
後臺程式目錄如下:
框架 | 解決方案 | 小程式 Controller 程式碼 | 學習新一代 .NET | |
.NET Framework 4.5 |
\Samples\net45-mvc\Senparc.Weixin.MP.Sample.sln |
Senparc.Weixin.Sample專案下 Controllers/WxOpenController.cs |
|
|
.NET Core 3.1 | \Samples\netcore3.0-mvc\Senparc.Weixin.Sample.NetCore3.vs2019.sln | 學習 .NET Core 3.1 | ||
.NET 6.0(相容5.0) | \Samples\net6-mvc\Senparc.Weixin.Sample.Net6.sln | 學習 .NET 6.0 |
線上 Demo:
小程式二維碼 |
注意點
1、建議將小程式基礎庫升級到最新,否則可能導致無法正確解密:
2、注意 wx.getUserProfile 介面和 wx.login 介面的呼叫次序,Demo 中為了方便演示各項介面能力,所以進行了如下的巢狀操作:
wx.getUserProfile({ desc: '用於完善會員資料', success: function (userInfoRes) { //... //呼叫 wx.login 登入介面 wx.login({ success: function (res) { //換取openid & session_key wx.request({ url: wx.getStorageSync('domainName') + '/WxOpen/OnLogin', method: 'POST', header: { 'content-type': 'application/x-www-form-urlencoded' }, data: { code: res.code }, success:function(json){ var result = json.data; if(result.success) { wx.setStorageSync('sessionId', result.sessionId); //校驗 wx.request({ url: wx.getStorageSync('domainName') + '/WxOpen/CheckWxOpenSignature', method: 'POST', header: { 'content-type': 'application/x-www-form-urlencoded' }, data: { sessionId: result.sessionId,//wx.getStorageSync('sessionId'), rawData:userInfoRes.rawData, signature:userInfoRes.signature }, success:function(json){ console.log(json.data); } }); //解密資料(建議放到校驗success回撥函式中,此處僅為演示) wx.request({ url: wx.getStorageSync('domainName') + '/WxOpen/DecodeEncryptedData', method: 'POST', header: { 'content-type': 'application/x-www-form-urlencoded' }, data: { 'type':"userInfo", sessionId: result.sessionId,//wx.getStorageSync('sessionId'), encryptedData: userInfoRes.encryptedData, iv: userInfoRes.iv }, success:function(json){ console.log('資料解密:', json.data); } }); }else{ console.log('儲存session失敗!',json); } } }) } }) } });
相關後端程式碼,如果是新專案,只需要按照之前的實現方式,舊專案不需要修改:
wx.login 成功後,呼叫 WxOpenController.cs 中的 OnLogin 方法:
1 /// <summary> 2 /// wx.login登陸成功之後傳送的請求 3 /// </summary> 4 /// <param name="code"></param> 5 /// <returns></returns> 6 [HttpPost] 7 public ActionResult OnLogin(string code) 8 { 9 try 10 { 11 var jsonResult = SnsApi.JsCode2Json(WxOpenAppId, WxOpenAppSecret, code); 12 if (jsonResult.errcode == ReturnCode.請求成功) 13 { 14 //Session["WxOpenUser"] = jsonResult;//使用Session儲存登陸資訊(不推薦) 15 //使用SessionContainer管理登入資訊(推薦) 16 var unionId = ""; 17 var sessionBag = SessionContainer.UpdateSession(null, jsonResult.openid, jsonResult.session_key, unionId); 18 19 //注意:生產環境下SessionKey屬於敏感資訊,不能進行傳輸! 20 return Json(new { success = true, msg = "OK", sessionId = sessionBag.Key, sessionKey = sessionBag.SessionKey }); 21 } 22 else 23 { 24 return Json(new { success = false, msg = jsonResult.errmsg }); 25 } 26 } 27 catch (Exception ex) 28 { 29 return Json(new { success = false, msg = ex.Message }); 30 } 31 }
OnLogin 方法呼叫成功後進行簽名校驗:
1 /// <summary> 2 /// 檢查簽名 3 /// </summary> 4 /// <param name="sessionId"></param> 5 /// <param name="rawData"></param> 6 /// <param name="signature"></param> 7 /// <returns></returns> 8 [HttpPost] 9 public ActionResult CheckWxOpenSignature(string sessionId, string rawData, string signature) 10 { 11 try 12 { 13 var checkSuccess = Senparc.Weixin.WxOpen.Helpers.EncryptHelper.CheckSignature(sessionId, rawData, signature); 14 return Json(new { success = checkSuccess, msg = checkSuccess ? "簽名校驗成功" : "簽名校驗失敗" }); 15 } 16 catch (Exception ex) 17 { 18 return Json(new { success = false, msg = ex.Message }); 19 } 20 }
同時進行使用者資訊解密及水印校驗:
1 /// <summary> 2 /// 資料解密並進行水印校驗 3 /// </summary> 4 /// <param name="type"></param> 5 /// <param name="sessionId"></param> 6 /// <param name="encryptedData"></param> 7 /// <param name="iv"></param> 8 /// <returns></returns> 9 [HttpPost] 10 public async Task<IActionResult> DecodeEncryptedData(string type, string sessionId, string encryptedData, string iv) 11 { 12 DecodeEntityBase decodedEntity = null; 13 14 try 15 { 16 switch (type.ToUpper()) 17 { 18 case "USERINFO"://wx.getUserInfo() 19 decodedEntity = EncryptHelper.DecodeUserInfoBySessionId( 20 sessionId, 21 encryptedData, iv); 22 break; 23 default: 24 break; 25 } 26 } 27 catch (Exception ex) 28 { 29 WeixinTrace.SendCustomLog("EncryptHelper.DecodeUserInfoBySessionId 方法出錯", 30 $@"sessionId: {sessionId} 31 encryptedData: {encryptedData} 32 iv: {iv} 33 sessionKey: { (await SessionContainer.CheckRegisteredAsync(sessionId) 34 ? (await SessionContainer.GetSessionAsync(sessionId)).SessionKey 35 : "未儲存sessionId")} 36 37 異常資訊: 38 {ex.ToString()} 39 "); 40 } 41 42 //檢驗水印 43 var checkWatermark = false; 44 if (decodedEntity != null) 45 { 46 checkWatermark = decodedEntity.CheckWatermark(WxOpenAppId); 47 48 //儲存使用者資訊(可選) 49 if (checkWatermark && decodedEntity is DecodedUserInfo decodedUserInfo) 50 { 51 var sessionBag = await SessionContainer.GetSessionAsync(sessionId); 52 if (sessionBag != null) 53 { 54 await SessionContainer.AddDecodedUserInfoAsync(sessionBag, decodedUserInfo); 55 } 56 } 57 } 58 59 //注意:此處僅為演示,敏感資訊請勿傳遞到客戶端! 60 return Json(new 61 { 62 success = checkWatermark, 63 //decodedEntity = decodedEntity, 64 msg = $"水印驗證:{(checkWatermark ? "通過" : "不通過")}" 65 }); 66 }
上述方法中標紅的介面呼叫、SessionKey管理和解密方法都已經封裝在 Senparc.Weixin SDK 中(更多教程),只需要按照 Demo 演示的呼叫即可。
除錯視窗結果:
注意:
1、不能在呼叫 wx.login 等過程的回撥函式中,自動呼叫 wx.getUserProfile 來觸發授權行為,因為 wx.getUserProfile 只能由使用者手動觸發。否則,系統會丟擲異常:
error msg: getUserProfile:fail can only be invoked by user TAP gesture
關於這兩個方法的同時使用問題也可以參考:《wx.getUserProfile不能和wx.login一起使用?》(PS:並非所有都是正解)
2、雖然官方提示需要使用2.10.4以上基礎庫,但是實測發現,2.10.4及之後的幾個版本,雖然可以使用wx.getUserProfile,但在使用者資訊解密(wx.login)上面都有缺陷,導致無法正常解密,因此,建議直接升級到最新的版本(見上圖)。升級基礎庫後,後端程式碼不需要修改。
下一篇我們將介紹微信公眾號模板訊息下線後,如何使用“訂閱訊息”進行開發。