.Net之微信小程序获取用户UnionID的实现

在实际项目开发中我们经常会遇到账号统一的问题,如何在不同端或者是不同的登录方式下保证同一个会员或者用户账号唯一(便于用户信息的管理)。这段时间就有一个这样的需求,之前有个客户做了一个微信小程序商城(店主端的),然后现在又要做一个会员购物端的小程序商场。首先之前用户登录凭证都是使用微信openid来做的唯一标识,而现在客户需求是要做到用户在会员端小程序跳转到到店主端小程序假如之前该用户微信是在店主端审核通过的用户则不需要在进行资料提交审核操作,直接登录。所以,所以我们使用了UnionID来进行关联,如下是我们现在项目的基本流程(画的丑莫见怪)。

.Net之微信小程序获取用户UnionID的实现

说说UnionID机制:

如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。

官方UnionID机制详细说明:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html

微信开放平台绑定小程序流程:

登录微信开放平台— 管理中心 — 小程序 — 绑定小程序(直接使用微信官方图)

.Net之微信小程序获取用户UnionID的实现

微信小程序获取UnoinID的两种方式

调用接口wx.getUserInfo,从解密数据(encryptedData)中获取 UnionID(推荐使用):

推荐使用原因:无需关注微信公众号即可获取到UnionID。

调用接口wx.getUserInfo前提:用户允许授权获取用户信息!

开发者后台校验与解密开放数据:

微信为了保证用户信息,把用户通过wx.getUserInfo接口获取到的相关敏感信息进行了加密。加密方式对称加密(后面会提到),首先我们需要通过微信小程序登录流程获取到用户的session_key(会话密钥),然后我们可以报获取到的会话密钥使用缓存存起来,在通过用户授权获取用户相关信息,如下是用户授权成功获取到的用户信息:

.Net之微信小程序获取用户UnionID的实现

基本流程图如下:

.Net之微信小程序获取用户UnionID的实现

(encryptedData)加密数据解密算法:

开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData)进行对称解密。 解密算法如下:

对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。

对称解密的目标密文为 Base64_Decode(encryptedData)。

对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。

对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回

很遗憾的是微信居然没有为我们大.Net提供解密算法demo,实属让人不算,最后自己根据网上的资料还是配上了符合微信对称加密的解密算法。

代码实现:

首先关于session_key(会话密钥)的获取,请看下面的wx.login+code2Session 方式

调用接口wx.getUserInfo获取encryptedData(加密数据)和iv(初始向量):

// 用户已经授权 wx.getUserInfo({ success: function(res) { console.log(res); var userInfo = res.userInfo //用户基本信息 let sessionKey = wx.getStorageSync("session_key");//临时会话密钥,通过小程序登录流程获取到的 //请求.net webapi解密接口 wx.request({ url: 'https://www.xxxtest.com/api/User_oAuth/DecryptSensitiveData', data: { sessionKey:sessionKey, encryptedData:res.encryptedData, iv:res.iv }, header: { 'content-type': 'application/json' // 默认值 }, success (res) { //解密返回过来的UnionID console.log(res.data) } }) } }) })

.Net WebApi 解密数据接口:

/// <summary> /// 解密微信对称加密数据,获取用户联合运营编号 /// </summary> /// <param>临时会话秘钥</param> /// <param>微信用户敏感加密数据</param> /// <param>解密初始向量</param> /// <returns></returns> [HttpGet] public IHttpActionResult DecryptSensitiveData(string sessionKey,string encryptedData,string iv) { try { var getUnionId=DecryptByAesBytes(encryptedData, sessionKey, iv); return Json(new { code =1, msg="解密成功",result= getUnionId }); } catch (Exception ex) { return Json(new { code = 0, msg = "解密失败,原因:"+ex.Message }); } } #region AES对称解密 /// <summary> /// AES解密 /// </summary> /// <param>待解密的字节数组</param> /// <param>解密密钥字节数组</param> /// <param>IV初始化向量字节数组</param> /// <param>运算模式</param> /// <param>填充模式</param> /// <returns></returns> private static string DecryptByAesBytes(string encryptedData, string sessionKey, string iv) { try { //非空验证 if (!string.IsNullOrWhiteSpace(encryptedData) && !string.IsNullOrWhiteSpace(sessionKey) && !string.IsNullOrWhiteSpace(iv)) { var decryptBytes = Convert.FromBase64String(encryptedData.Replace(' ', '+')); var keyBytes = Convert.FromBase64String(sessionKey.Replace(' ', '+')); var ivBytes = Convert.FromBase64String(iv.Replace(' ', '+')); var aes = new AesCryptoServiceProvider { Key = keyBytes, IV = ivBytes, Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 }; var outputBytes = aes.CreateDecryptor().TransformFinalBlock(decryptBytes, 0, decryptBytes.Length); var decryptResult = Encoding.UTF8.GetString(outputBytes); dynamic decryptData = JsonConvert.DeserializeObject(decryptResult, new { unionid = "" }.GetType()); JJHL.Utility.Loghelper.WriteLog("AES对称解密结果为:" + decryptResult); return decryptData.unionid; } else { return ""; } } catch (Exception e) { JJHL.Utility.Loghelper.WriteLog("AES对称解密失败原因:" + e.Message); return ""; } } #endregion

所遇异常:参数使用Convert.FromBase64String转化时,提示“Base-64字符数组的无效长度” 的问题:

原因:加密参数中的"+"通过地址栏传过来时,后台会解析为空格(遇到的概率比较小)。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdszdp.html