0%

关于 openId 和 unionId 的那些事

官方文档

开发小程序, openId 和 unionId 几乎是没法绕过去的, 但官方文档又讲得不是那么详细,还是自己总结一下以备自己哪天忘了

简介

openId: 用户对于某一应用里的唯一标识, 如同一用户的订阅号, 服务好, 小程序1, 小程序2的 openId 是相互独立的. openId 可以静默获取.

unionId: 在同一个微信开放平台里绑定的移动应用, 网站应用, 公众号, 小程序或第三方平台, 通过一样的 unionId 来标识同一个用户. unionId 的获取需要得到用户的授权, 或用户曾在同一平台的其他应用登录/授权过.

所以, 如果只有一个应用, 那么 openId 是够用的, 但为了后续开发方便, 还是使用 unionId 比较好.

ps: openId 长度为12, unionId 长度为28

如何获取 openId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
wx.login({
success: loginCode => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId, 注意:一个code只能用一次
getApp().getOpenId(loginCode);
}
/*这里的获取只能在测试环境中实现, 实际应用时因为 api.weixin.qq.com 不在微信许可的域名中,
所以需要把 loginCode.code 传给服务器, 让服务器向微信请求获得 appid*/
getOpenId: function(loginCode){
var that = this;
var appid = 'wx19110c392ee93159';//小程序appid
var secret = '9223a34383d998f0d531ec796c297ead';//小程序secret
wx.request({
url: 'https://api.weixin.qq.com/sns/jscode2session?appid=' + appid + '&secret=' + secret +'&grant_type=authorization_code&js_code=' + loginCode.code,
header: {
'content-type': 'application/json'
},
success: function (res) {
that.globalData.openId = res.data.openid;//获取openid
console.log('openid in app.js: '+that.globalData.openId);
}
})
},

关于 api 返回的数据:

不满足 unionId 下发条件的情况下返回的参数:

1
2
3
4
{
"openid": "OPENID", //用户唯一标识
"session_key": "SESSIONKEY", //会话密钥
}

满足 unionId 下发条件的情况下返回的参数:

1
2
3
4
5
{
"openid": "OPENID",
"session_key": "SESSIONKEY",
"unionid": "UNIONID" //用户在开放平台的唯一标识符
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//服务端代码
//appid 和 secret 被包装在 appIds 中
public void requestOpenid(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
resp.setContentType("text/html:charset=utf8");
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();

//获得loginCode, 用这个code可以换取 session_key 和 openid, 这个code是一次性的
String loginCode = new String(req.getParameter("loginCode").getBytes("iso-8859-1"), "UTF-8");

//向微信服务器发起GET请求
try{
StringBuilder sb = new StringBuilder();
URL url = new URL("https://api.weixin.qq.com/sns/jscode2session?appid=" + appIds.getMasterOrderId() + "&secret=" + appIds.getMasterOrderSecret() + "&grant_type=authorization_code&js_code=" + loginCode);
InputStream in = new BufferedInputStream(url.openStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
String line;
while((line=reader.readLine())!=null){
sb.append(line);
}
JSONObject json = JSONObject.fromObject(sb.toString());
session_key = json.get("session_key").toString();
openid = json.get("openid").toString();
unionid = json.get("unionid").toString();
}catch (Exception e){
e.printStackTrace();
}
}

如何获取 unionId

这里得分两种情况:

用户初次在该平台的应用登录

需要调用接口 wx.getUserInfo, 从解密数据中获取 unionId( 注意处理用户拒绝授权的情况 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//wx.getUserInfo 不再被建议使用, 需要通过按钮来获取
getUserInfo: function(e){//这里还应接入服务器, 将 sessionKey 传入服务器, 获取 unionId, 查询该用户状态
var that = this;
if (e.detail.userInfo) {
app.getOpenId(e.detail.signature),
app.globalData.userInfo = e.detail.userInfo,
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
}),
console.log(e.detail)//这里面就有包括 encryptedData
wx.navigateTo({
url: '../subInfo/subInfo',
})
}else{
app.toastShow(that, "小程序需要您的授权才能正常使用, 请重新授权", "", 2000)
}
},

用户授权后返回的信息里 encryptedData 解密后结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"openId": "OPENID",
"nickName": "NICKNAME",
"gender": GENDER,
"city": "CITY",
"province": "PROVINCE",
"country": "COUNTRY",
"avatarUrl": "AVATARURL",
"unionId": "UNIONID",
"watermark":
{
"appid":"APPID",
"timestamp":TIMESTAMP
}
}

用户已经关注了公众号, 或曾登录同一平台内的其他应用

直接通过 wx.login 获取到该用户的 unionId, 无需用户授权

关于 wx.getUserInfo

官方文档

直接调用该接口不会再出现授权弹窗:

用户未授权, 调用这个接口直接进入 fail 回调

用户授权过, 可以通过这个接口获得用户信息

将这个方法写再 welcome 界面的判断中

解密 encryptedData 后没有 unionId?

微信毒瘤, 没说清楚 unionId 的必要条件, 艹: 将小程序绑定到微信开放平台

如果暂时不想交那300块, 那么要求小程序主体, 及开放平台主体是一样的, 及拥有者是同一人

如何解密 unionId [转]

需要元素: 加密的数据: encryptedData, 密钥: session_key( 通过 wx.login 获得的临时登录凭证 loginCode 向微信请求后获得, session_key 有时效性 ), 初始向量: iv

详细解密步骤见参考链接:

参考链接( 都是 java 的):

微信小程序之用户数据解密(七)

微信小程序之获取并解密用户数据(获取openId、unionId)

顺便吐槽一下, 他们的代码块怎么这么乱(码)…

可能会缺失的包( 由maven引入 ):

1
2
3
4
5
6
7
8
9
10
11
12
<!-- AES.class解密用的包 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.60</version>
</dependency>
<!-- 引用解密包所需 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>

后续问题

微信小程序会记住用户是否授权过, 当用户第一次授权时, 通过服务器解密可以获取到用户的 unionid. 此时如果用户继续操作, 进行注册操作, 那么将 unionid, 用户注册信息存入临时用户表, 等待管理员确认.

用户第二次进入小程序, 通过 wx.getUserInfo 获得数据, 此时用户未得到确认, 进入等待页面

管理员确认后, unionid, 用户注册信息存入正式用户表

用户第三次进入小程序, 同样通过 ws.getUserInfo 获得数据, 经过确认的用户可以进入 shop 页面

关于要不要用用户表 id 来进行小程序与服务器进行对接: 目前觉得这样要多几步判断, 还是继续用 unionid 吧

详细图解见 微信小程序登录流程控制