自我介绍
我是IT果果日记,微信公众号请搜索 IT果果日记
一个普通的技术宅,定期分享技术文章,欢迎点赞、关注和转发,请多关照。
微信小程序用户基本信息有哪些?
除了基本信息,微信还会提供openId和unionId,它们有什么作用和区别?
在获取用户信息的过程中,如何知道明文数据是否被篡改了?
session_key 该如何使用呢?
带着这些问题,我们开始今天的学习。
获取用户基本信息
小程序提供了一个 wx.getUserProfile(OBJECT) 方法来获取用户的信息。以前的老版本是用的 wx.getUserProfile(OBJECT) 方法,现在以及被官方不推荐使用了。
用户信息分为用户基本信息和用户openId、UnionId。基本信息是明文的,而openId和UnionId是加密数据。这两种类型的数据都由wx.getUserProfile(OBJECT)方法返回。
用户基本信息包括以下7项:
avatarUrl 用户微信头像的URL地址。
city 城市。
country 国家。
gender 性别,1表示男,2表示女,0表示未知。
language 语言区域。
nickName 昵称。
province:省份。
在小程序中,用户的基本信息可以轻易获得,他们是明文的、不加密的。但 openId 和 UnionId 是加密的。什么是openId和UnionId呢?可以将openId和UnionId理解为用户在微信应用中的id号。
他们的区别是:
openId只代表用户在某个微信应用下的id号;
而UnionId是跨应用的,同一用户在同一开发者的多个应用里,UnionId是唯一的。
官方文档对于UnionId的描述非常清楚:如果开发者拥有多个移动应用、网站应用和公众账号(包括小程序),可通过UnionId区分用户的唯一性,因为只要是同一个微信开放平台账号下的移动应用、网站应用和公众账号(包括小程序),用户的UnionId就是唯一的。换句话说,同一用户在同一个微信开放平台下的不同应用中,UnionId是相同的。
所以,openId不能跨应用,如果要在多应用间统一用户身份,请使用UnionId。这里要注意,在小程序中使用UnionId首先需要前往微信开放平台绑定小程序。请参考官方文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
微信开放平台绑定小程序流程
登录微信开放平台 — 管理中心 — 小程序 — 绑定小程序
微信开放平台绑定小程序
下面来看wx.getUserProfile(OBJECT)方法的使用。wx.getUserProfile的OBJECT参数有3个回调函数:success、fail和complete。在这3个回调函数中,重点介绍success。success方法的返回值有以下5个:
userInfo 用户基本信息对象,不包含openId等敏感信息。
rawData 不包含敏感信息的基本信息字符串,通常用来计算签名,防止从微信返回的用户信息被篡改。
signature 使用sha1(rawData+sessionkey)得到字符串,用于校验用户信息。
encryptedData 包括敏感数据在内的完整用户信息的加密数据。
iv 加密算法的初始向量。
现在,我们在代码中获取用户的明文信息。代码如下:
_getUserInfo: function() {
var userInfoStorage= wx.getStorageSync(user);
if (!userInfoStorage) {
//如果缓存中没有用户信息,那么获取用户信息
var that = this;
wx.login({
success: function (){
wx.getUserProfile({
success: function (res){
that.globalData.userInfo = res.userInfo
//将用户的基本信息保存到缓存中
wx.setStorageSync('user',res.userInfo)
},
fail: function (res){
console.log(res);
}
})
}
})
}
else {
//如果缓存中已经存在用户的基本信息,那么将信息保存到全局变量中
this.globalData.userInfo = userInfoStorage;
}
}
解释一下上述代码。为了避免每次启动小程序时都要在微信服务器中加载用户的基本信息,我们将用户信息保存到缓存中。这样,每次启动小程序时先去看看缓存有没有数据,如果没有,就调用wx.getUserProfile获取用户信息并保存到缓存和全局变量userInfo中;如果有,就直接将用户信息保存到全局变量userInfo里。
细心的朋友可能注意到,我们并不是直接调用wx.getUserProfile(OBJECT)方法,而是先调用wx.login,在wx.login调用成功后再继续调用wx.getUserProfile。之所以在wx.login调用成功后才调用wx.getUserProfile,是因为官方文档明确指出了获取用户信息需要先调用wx.login。同时,在官方的示例项目中也是先调用wx.login。至于wx.login有什么作用,请耐心的往下看,大家只需“依葫芦画瓢”,照做即可。
事实上,如果你只想获取用户的明文基本信息,那么不调用wx.login,直接调用wx.getUserProfile也可以获取用户信息。所以,是否在调用wx.getUserProfile前调用wx.login请大家自行决定。
当第一次运行以上代码并调用wx.getUserProfile时,小程序会弹出如图所示的提示窗口,让用户选择是否授权获取用户的基本信息。
用户授权获取信息
用户点击“允许”后,才会执行wx.getUserProfile的success回调函数,如果点击“拒绝”,将执行fail回调函数。
有些开发者在测试时可能永远不会弹出这个授权窗口。因为如果当前项目没有appId,那么用户信息是由微信模拟的信息,并没有进行真实的获取用户信息流程,所以不会出现这个窗口,且开发工具会有一个警告提示,如图所示。
没有appId时是开发工具模拟的用户数据
当然,这个用户数据也是真实的用户数据,因为你在使用开发工具时必须扫描二维码登录,这样开发工具就能知道你的微信身份,自然可以模拟返回你的用户数据。但模拟数据相比于真实的用户数据缺少3个属性,即signature、encryptedData和iv。也就是说,模拟数据只有明文信息而没有加密信息。
将用户信息保存到缓存中有一个缺点,就是没办法实时更新用户信息(比如用户更改了自己的微信资料),但这是具体业务的问题,需要开发者在实际项目编写中灵活处理。
用户信息校验
上文已经介绍了如何调用微信登录接口以及获取用户信息。另外,在我以往的文章中也介绍了如何通过登录接口返回的code获取到session_key。之前提了一个问题,session_key是做什么用的呢?下面就讲解一下session_key的作用以及它的用法。
先来看看wx.getUserProfile返回的数据:
userInfo 用户基本信息对象,不包含openId等敏感信息。
rawData 不包含敏感信息的基本信息字符串,通常用来计算签名,防止从微信返回的用户信息被篡改。
signature 使用sha1(rawData+sessionkey)得到字符串,用于校验用户信息。
encryptedData 包括敏感数据在内的完整用户信息的加密数据。
iv 加密算法的初始向量。
我们使用了userInfo对象,包括userInfo和rawData在内的明文数据都可能存在被篡改的风险。如何知道明文数据是否被篡改了呢?
这个时候rawData和singature就可以发挥作用了。rawData和signature用于校验用户数据到底有没有被篡改过(没有绝对安全的网络,数据极有可能被抓包或者通过其他方式篡改)。通常来说,想要实现这个校验必须在服务器编码才能进行。这需要小程序将获取的rawData和signature一并提交到服务器,由服务器完成校验工作。
校验的基本原理是:rawData是用户原始明文数据,signature是使用sha1(rawData+sessionkey)得到的字符串。理论上讲,如果数据没有被篡改,那么signature等于sha1(rawData+sessionkey);如果rawData或者signature被修改了,那么signature必然不再等于sha1(rawData+sessionkey)。
是否存在signature和rawData同时被修改的情况呢?理论上是不可能的,因为session_key并不在网络上传输,篡改者不知道这个变量,被篡改且校验通过的概率很小。
有可能从signature中推算出session_key吗?理论上讲,这是不可能的。因为sha1算法是不可逆的,无法在已知rawData和signature的情况下推算出session_key。不知道session_key就无法通过同时修改rawData和signature达到“欺骗校验的目的”。如果知道了session_key,只需要修改rawData并重新用session_key计算一下新的sha1(rawData+session_key)就又可以让新的rawData等于新的sha1(rawData+session_key)了。这样,开发者就无法知道rawData是被修改过的。
这也是为什么官方文档一再强调,不要在网络上传输session_key,而应该将其保存在服务器上使用,以降低session_key被泄露的风险。
session_key有点类似于我们在数据库中保存用户密码时所使用的“盐(salt)”。在数据库保存用户密码时,并不是直接将用户的密码以明文的方式存放在数据库表中,通常都会使用SHA-1或者MD5算法将用户密码和salt随机字符串拼接在一起,重新计算一下再存入数据库中。被重新使用SHA-1或MD5算法计算的用户密码谁都不知道是什么,开发者也只能比对每次登录时输入的密码和数据库保存的密码是否一致,判断是否为合法用户,却无法知道密码到底是什么。
用户数据校验流程图
需要说明的是,我们明确说明服务器是没有保存session_key的。因为我们需要拿到session_key才能进行用户数据校验,所以在上述流程图再一次重复了用户的登录流程。在真实的流程中,用户登录在session_key的有效时间内只应该执行一次,session_key也应当被保存在服务器中。其实小程序只需要使用wx.request将rawData和signature发送到服务器即可,服务器无须使用code换取session_key,直接做SHA-1签名比对即可。如图所示为服务器已保存session_key的用户数据校验流程。
如果服务器已保存了session_key的用户数据校验流程
对比服务器没保存session_key和保存了session_key的数据校验流程图,很明显可以看出,服务器保存session_key后整个流程变得更加简单,完全不需要再与微信服务器交互。
服务器管理session_key的过程非常复杂,为了降低代码的复杂度,我们没有在服务器中保存session_key。我们后续的所有开放API调用流程都将重复完整的流程,因为单独完整的流程更加有利于开发者理解每个开放API。下面看下具体代码:
前端代码
wx.login({
success: function (loginRes){
wx.getUserProfile({
success: function (userRes){
wx.request({
url: "http://localhost:8080/wxopen/wxcheckuserinfo"
data: {
code: loginRes.code,
signature: userRes.signature,
rawData: userRes.rawData
},
success: function(res){
console.log(res.data);
}
})
}
})
}
})
首先,登录并拿到code码,然后调用wx.getUserProfile接口到rawData和signature,再使用wx.request将这3个参数发送到服务器中,服务器会进行数据校验工作并返回校验结果。下面是服务器接口wxcheckuserinfo的编码。
Map<String,Object> map = new HashMap<>(7);
map.put("appid",wxAppId);
map.put("secret",wxSecret);
map.put("js_code", param.getCode());
map.put("grant_type",grantType);
WxCode2SessionRet result = null;
try{
String url = "https://api.weixin.qq.com/sns/jscode2session";
String info = HttpUtil.get(url, map);
result = JSON.parseObject(info, WxCode2SessionRet.class);
} catch (Exception e){
log.error("code2session失败", e);
return null;
}
try {
String signature2 = DigestUtils.sha1Hex(encryptedData + result.getSession_key());
if (!signature.equals(signature2)) {
return R.error().message("签名校验失败");
}
} catch (Exception e) {
throw new RuntimeException("用户信息校验失败");
}
WxCode2SessionRet.java
@Data
public class WxCode2SessionRet implements Serializable {
private String openid;
private String session_key;
private String unionid;
private String errcode;
private String errmsg;
}
在以上代码中,首先使用code调用微信服务器换取session_key,随后使用session_key和signarue校验用户发送过来的rawData,最后返回校验结果。
建议开发者在客户端使用用户明文数据时使用rawData,而不要使用userInfo。因为数据验证的是rawData有没有被篡改,而不是验证userInfo是否被篡改。至于微信能否确保userInfo和rawData的一致性,这个不得而知。建议开发者使用rawData作为用户的基本信息。
至此,微信小程序获取用户信息、校验用户信息的过程就已经讲完了,下一篇文章我会介绍一下怎么解析用户的加密信息。
总结
用户基本信息有以下7个,他们属于明文
avatarUrl 用户微信头像的URL地址。
city 城市。
country 国家。
gender 性别,1表示男,2表示女,0表示未知。
language 语言区域。
nickName 昵称。
province:省份。
用户加密信息有openId和UnionId,它们是用户在微信应用中的id号
他们的区别是:
1. openId只代表用户在某个微信应用下的id号;
2. 而UnionId是跨应用的,同一用户在同一开发者的多个应用里,UnionId是唯一的。
如何知道明文数据是否被篡改了?通过校验用户加密信息rawData和signature是否一致。signature是使用sha1(rawData+sessionkey)得到的字符串。如果数据没有被篡改,那么signature等于sha1(rawData+sessionkey);如果数据被篡改,则不等于。
session_key有什么作用?session_key有点类似于我们在数据库中保存用户密码时所使用的“盐(salt)”。
请联系我
我是IT果果日记,微信公众号请搜索 IT果果日记
一个普通的技术宅,定期分享技术文章,欢迎点赞、关注和转发,请多关照。
微信公众号 IT果果日记
https://gitee.com/chenzhaoplus
https://github.com/chenzhaoplus
https://blog.csdn.net/cz285933169?spm=1010.2135.3001.5421