微信公众号 IT果果日记
前言
在上一篇文章“微信小程序如何获取用户信息”中我们完成了用户明文数据的校验工作,本文将学习解密用户的非明文用户信息,也就是获取用户的openId和unionId。
解密
调用wx.getUserProfile后将返回encryptedData和iv两个数据。encryptedData是包括敏感数据在内的完整用户信息的加密数据,iv用于解密这个数据。整个解密用户数据的过程同上篇文章中用户信息校验的流程基本相同。不同的是,我们提交到服务器的数据是encryptedData和iv,而不是signature和rawData。下面编写用户数据解密的核心代码:
wx.login({
success: function (loginRes){
wx.getUserProfile({
success: function (userRes){
wx.request({
url: "http://localhost:8080/wxopen/wxdecryptuserinfo"
data: {
code: loginRes.code,
encryptedData: userRes.encryptedData,
iv: userRes.iv
},
success: function(res){
console.log(res.data);
}
})
}
})
}
})
以上代码将code(用于获取session_key)、encryptedData和iv 3个参数发送到了后端接口wxdecryptuserinfo中。
服务器如何解密encryptedData数据呢?解密时需要session_key、iv、小程序的AppId 3个变量参与。session_key由code可以换取到,iv由小程序客户端提交,AppId本身就是一个固定的已知变量。
具体的解密算法较为复杂,但微信官方提供了包括C++、NodeJS、PHP和Python 4种语言的解密SDK,我们只需要使用官方提供的SDK即可,无须自己编写解密算法。目前,官方没有提供JAVA和C#版本的SDK,开发者可自行翻译。下面是java的解密代码实现:
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 result = AesCbcUtil.decrypt(param.getEncryptedData(),
result.getSession_key(), param.getIv(), "UTF-8");
JSONObject wxInfo = JSONObject.parseObject(result);
String unionId = wxInfo.getString("unionId");
String openid = sessionRet.getOpenid();
} catch (Exception e) {
throw new RuntimeException("用户信息校验失败");
}
AesCbcUtil.decrypt 解密方法
public static String decrypt(String data, String key, String iv, String encodingFormat) {
//被加密的数据
byte[] dataByte = Base64.decodeBase64(data);
//加密秘钥
byte[] keyByte = Base64.decodeBase64(key);
//偏移量
byte[] ivByte = Base64.decodeBase64(iv);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, encodingFormat);
return result;
}
return null;
} catch (Exception e) {
log.error("解密失败",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;
}
这样,就可以从加密数据encryptedData里解密出我们想要的UnionId和openId了。
微信公众号 IT果果日记
https://gitee.com/chenzhaoplus
https://github.com/chenzhaoplus
https://blog.youkuaiyun.com/cz285933169?spm=1010.2135.3001.5421