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

说说UnionID机制:
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
官方UnionID机制详细说明:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
微信开放平台绑定小程序流程:
登录微信开放平台 — 管理中心 — 小程序 — 绑定小程序(直接使用微信官方图)

微信小程序获取UnoinID的两种方式:
调用接口 wx.getUserInfo,从解密数据(encryptedData)中获取 UnionID(推荐使用):
推荐使用原因:无需关注微信公众号即可获取到UnionID。
调用接口wx.getUserInfo前提:用户允许授权获取用户信息!
开发者后台校验与解密开放数据:
微信为了保证用户信息,把用户通过wx.getUserInfo接口获取到的相关敏感信息进行了加密。加密方式对称加密(后面会提到),首先我们需要通过微信小程序登录流程获取到用户的session_key(会话密钥),然后我们可以报获取到的会话密钥使用缓存存起来,在通过用户授权获取用户相关信息,如下是用户授权成功获取到的用户信息:

基本流程图如下:

(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 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 =, msg="解密成功",result= getUnionId });
}
catch (Exception ex)
{
return Json(new { code = , 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, , 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 == ) {
//存入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 = , 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
.Net之微信小程序获取用户UnionID的更多相关文章
- 微信小程序——获取用户unionId
1.获取code 2.获取openid 3.获取access_token 4.获取unionid
- [微信小程序] 微信小程序获取用户定位信息并加载对应城市信息,wx.getLocation,腾讯地图小程序api,微信小程序经纬度逆解析地理信息
因为需要在小程序加个定位并加载对应城市信息 然而小程序自带api目前只能获取经纬度不能逆解析,虽然自己解析方式,但是同时也要调用地图,难道用户每次进小程序还要强行打开地图选择地址才定位吗?多麻烦也不利 ...
- 微信小程序 获取用户信息并保存登录状态
微信小程序 获取用户信息并保存登录状态:http://www.360doc.com/content/18/0124/11/9200790_724662071.shtml
- Laravel wxxcx 微信小程序获取用户信息
wxxcx 是Laravel5微信小程序登录获取用户信息扩展 部署 12345678 # 安装$ composer require iwanli/wxxcx# 注册服务# 在 /config/app. ...
- 微信小程序 获取用户信息 encryptData解密 C#版本
最近学习小程序开发,需要对encryptData解密,获取用户信息,官方源码没有C#版本,网上的资料比较杂,有的使用还有问题,下面贴一下自己亲试可以使用的一个源码 1.code 换取 session_ ...
- 微信小程序获取用户手机号详解
最近在做一款微信小程序,需要获取用户手机号,具体步骤如下: 流程图: 1.首先,客户端调用wx.login,回调数据了包含jscode,用于获取openid(用户唯一标识)和sessionkey(会话 ...
- 微信小程序获取用户手机号
获取微信用户绑定的手机号,需先调用wx.login接口. 小程序获取code. 后台得到session_key,openid. 组件触发getPhoneNumber 因为需要用户主动触发才能发起获取手 ...
- 微信小程序 获取用户openid
1,可以在小程序app.js入口文件中放入登录代码 wx.login({ success: res => { // 登录注册接口 if (res.code) { // 调用服务端登录接口,发送 ...
- 微信小程序获取用户信息,解密encryptedData 包括敏感数据在内的完整用户信息的加密数据
package com.iups.wx.wxservice; import java.io.UnsupportedEncodingException; import java.security.Alg ...
随机推荐
- JAVA-注解(2)-自定义注解及反射注解
自定义注解开发 1.开发一个注解类 开发一个注解类的过程,非常类似于开发一个接口,只不过需要通过@interface关键字来声明 2.使用元注解修饰注解的声明 所谓的原注解是用来修饰注解声明的注释,可 ...
- 2019前端面试系列——JS面试题
判断 js 类型的方式 1. typeof 可以判断出'string','number','boolean','undefined','symbol' 但判断 typeof(null) 时值为 'ob ...
- Oracle 数据库登录、用户解锁、改密码、创建用户授权操作
一.数据库登录1.常用账户: 管理员: sys主要练习操作用户: scott2.测试环境是否配置成功: 1.命令窗口 win+R -> cmd(以管理员身份运行) - > sqlplus ...
- python整形及浮点型求余数的区别
1.代码如下 a=7.0b=4.0c=7e=4 #整形求余print("%d/%d=%d" %(c,e,c/e)) #将浮点型强制转换为整形,余数用浮点型表示print(" ...
- Appium+python自动化(二十六)- 烟花一瞬,昙花一现 -Toats提示(超详解)
简介 今天宏哥在这里首先给小伙伴们和童鞋们分享一个有关昙花的小典故:话说昙花原是一位花神,她每天都开花,四季都灿烂.她还爱上了每天给她浇水除草的年轻人.后来,此事给玉帝得知.于是,玉帝大发雷霆,要拆散 ...
- 【JDK】JDK源码分析-AbstractQueuedSynchronizer(3)
概述 前文「JDK源码分析-AbstractQueuedSynchronizer(2)」分析了 AQS 在独占模式下获取资源的流程,本文分析共享模式下的相关操作. 其实二者的操作大部分是类似的,理解了 ...
- Ubuntu 10.04下实现双网卡负载均衡
摘要:本文主要介绍和配置 在Ubuntu下 实现 bonding,双网卡负载,bonding模式为0,好处是负载平衡,另一网卡断了,也能工作. 什么是bonding Linux bonding 驱动提 ...
- Go中配置文件读取的几种方式
日常开发中读取配置文件包含以下几种格式: json 格式字符串 K=V 键值对 xml 文件 yml 格式文件 toml 格式文件 前面两种书写简单,解析过程也比较简单.xml形式书写比较累赘,yml ...
- 用 bat 文件实现 excel 周报复制
又要写周报???? 写周报就算了每次都要改这一大堆的日期,什么鬼嘛,最骚的我还总是有的忘记改.... 作为一个正儿八经的程序员,固定每周某天干重复的一件事,哦~~ 这是机器人 程序应 ...
- Multiple dex files define Lokhttp3/internal/wsWebSocketProtocol
Multiple dex files define Lokhttp3/internal/wsWebSocketProtocol 老套路,先晒图 图一:如题,在编译打包时遇到了如上错误,很明显这是一个依 ...