.NET之微信小程序获取用户UnionID

Wesley13
• 阅读 650

前言:

  在实际项目开发中我们经常会遇到账号统一的问题,如何在不同端或者是不同的登录方式下保证同一个会员或者用户账号唯一(便于用户信息的管理)。这段时间就有一个这样的需求,之前有个客户做了一个微信小程序商城(店主端的),然后现在又要做一个会员购物端的小程序商场。首先之前用户登录凭证都是使用微信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) 进行对称解密。 解密算法如下:

  1. 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
  2. 对称解密的目标密文为 Base64_Decode(encryptedData)。
  3. 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
  4. 对称解密算法初始向量 为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 name="sessionKey">临时会话秘钥</param>
        /// <param name="encryptedData">微信用户敏感加密数据</param>
        /// <param name="iv">解密初始向量</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 name="encryptedData">待解密的字节数组</param>
        /// <param name="sessionKey">解密密钥字节数组</param>
        /// <param name="iv">IV初始化向量字节数组</param>
        /// <param name="cipher">运算模式</param>
        /// <param name="padding">填充模式</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字符数组的无效长度” 的问题:

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

解决:最好的做法是 使用encryptedData.Replace("+", "%2B")先将空格编码,然后再作为参数传给另一页面传递,这样页面在提取参数时才会将“%2B”解码为加号.但这儿为了简化,将空格直接还原为"+"或者是直接在后台将空格替换为“+”encryptedData.Replace('  ', '+');

直接通过 wx.login + code2Session 获取到该用户 UnionID:

其实这个方式就是实现了小程序的登录流程,微信官方详细说明:

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

优点:无需用户授权。

前提:用户需要关注该微信公众号。

小程序端调用接口wx.login获取code凭证,在通过请求auth.code2Session接口获取用户信息(UnionID,openid,session_key会话密钥)两种方式:

1.直接通过wx.login请求到code凭证后,在请求该地址获取用户信息:
GET:https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

详细说明请看微信官方文档(代码略):https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

2.通过请求wx.login获取code凭证,在向.net webapi后端请求code2Session接口:

原因:因为我们需要对获取的用户信息做相关业务逻辑处理。

微信小程序端代码:
/**
*封装用户promise登录,通过code凭证获取用户信息(UnionID,openid,session_key会话密钥)
*/
userLogin: function() {
var that = this;
//定义promise方法
return new Promise(function(resolve, reject) {
//调用登录接口
wx.login({
success: function(res) {
if (res.code) {
console.log("用户登录授权code为:" + res.code);
//调用wx.request请求传递code凭证换取用户openid,并获取后台用户信息
wx.request({
url: 'https://www.xxxx.xxx.api/User_oAuth/GetUserInfo',//后台请求用户信息方法
data: {
code: res.code //code凭证
},
header: {
'content-type':'application/json' // 默认值
},
success(res) {
console.log(res.data)
if (res.data.errcode == 0) {
//存入session缓存中
console.log(res.data.openid);//微信用户唯一标识
console.log(res.data.UnionID);//微信开发平台联合ID
console.log(res.data.session_key);//会话密钥//***注意****//注意:这里是直接把session_key缓存起来,在上面wx.getUserInfo会使用到wx.setStorageSync("session_key",res.data.session_key);

//promise机制放回成功数据 resolve(res.data); } else { reject('error'); } }, fail: function(res) { reject(res); wx.showToast({ title: '系统错误' }) }, complete: () => { } //complete接口执行后的回调函数,无论成功失败都会调用 }) } else { reject("error"); }}})
})}
.Net WebApi 请求用户信息接口:
/// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="code">信息数据code凭证</param>
        /// <returns></returns>
        [HttpGet]
        public IHttpActionResult GetUserInfo(string code)
        {
            string AppSecret = "小程序秘钥";
            string AppId = "应用程序标识";

            try
            {
                //请求目标地址和参数(authorization_code授权类型,此处只需填写 authorization_code)

string OauthUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + AppId + "&secret=" + AppSecret + "&js_code=" + code + "&grant_type=authorization_code";//序列化解析数据
                var Result = HttpGet(OauthUrl);

                return Json(new { openid = Result.openid, errcode = Result.errcode, UnionID = Result.unionid, session_key = Result.session_key });
            }
            catch (Exception ex)
            {

                return Json(new { errcode = 1, msg = "获取用户信息失败" + ex.Message });
            }
        }

        /// <summary>
        /// 请求code2Session接口获取用户信息
        /// </summary>
        /// <param name="requestDataAndUrl">目标地址和参数</param>
        /// <returns></returns>
        public WxOauthModle HttpGet(string requestDataAndUrl)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestDataAndUrl);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return JsonConvert.DeserializeObject<WxOauthModle>(retString);
        }

        public class WxOauthModle
        {
            /// <summary>
            /// 用户唯一标识
            /// </summary>
            public string openid { get; set; }

            /// <summary>
            /// 会话秘钥
            /// </summary>
            public string session_key { get; set; }

            /// <summary>
            /// 联立编号
            /// </summary>
            public string unionid { get; set; }

            /// <summary>
            /// 错误码
            /// </summary>
            public int errcode { get; set; }

            /// <summary>
            /// 错误信息
            /// </summary>
            public string errmsg { get; set; }
        }

关于微信网页开发通过UnionID机制解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题:

详情说明请点击:https://www.cnblogs.com/Can-daydayup/p/9368844.html

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
马丁路德 马丁路德
3年前
小程序静默登录与维护自定义登录态
1.背景在小程序中,openid是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户,就如同你的身份证一样。2.什么是静默登录?在普通的应用中,用户通过表单验证登录建立用户体系,这种常见的登录方式一般是通过登录页面表单进行登录,对用户来说是有感的。在小程序中,由于是基于微信,可以通过微信官方提供的API能力,使我们能够无感知得获取
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
梦
3年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
Easter79 Easter79
2年前
thinkcmf+jsapi 实现微信支付
首先从小程序端接收订单号、金额等参数,然后后台进行统一下单,把微信支付的订单号返回,在把订单号发送给前台,前台拉起支付,返回参数后更改支付状态。。。回调publicfunctionnotify(){$wechatDb::name('wechat')where('status',1)find();
Easter79 Easter79
2年前
Taro小程序自定义顶部导航栏
微信自带的顶部导航栏是无法支持自定义icon和增加元素的,在开发小程序的时候自带的根本满足不了需求,分享一个封装好的组件,支持自定义icon、扩展dom,适配安卓、ios、h5,全面屏。我用的是京东的Taro多端编译框架写的小程序,原生的也可以适用,用到的微信/taro的api做调整就行,实现效果如下。!在这里插入图片描述(https://i
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这