原創聲明:本文為作者原創,未經允許不得轉載,經授權轉載需注明作者和出處
經常看到有點的小伙伴在群里問小程序用戶數據解密流程,所以打算寫一篇關于小程序用戶敏感數據解密教程;
加密過程微信服務器完成,解密過程在小程序和自身服務器完成,即由 encryptData 得到如下數據:
{
"openId": "OPENID",
"nickName": "NICKNAME",
"gender": GENDER,
"city": "CITY",
"province": "PROVINCE",
"country": "COUNTRY",
"avatarUrl": "AVATARURL",
"unionId": "UNIONID",
"watermark":
{
"appid":"APPID",
"timestamp":TIMESTAMP
}
}
準備知識:
以上3點對于理解解密流程非常重要。
根據官方文檔,我梳理了大致的解密流程,如下:
重點在6、7、8三個環節。
AES解密三個參數:
服務端解密流程:
下面結合小程序實例說明解密流程:
var that = this;
wx.login({
success: function (res) {
//微信js_code
that.setData({wxcode: res.code});
//獲取用戶信息
wx.getUserInfo({
success: function (res) {
//獲取用戶敏感數據密文和偏移向量
that.setData({encryptedData: res.encryptedData})
that.setData({iv: res.iv})
}
})
}
})
var httpclient = require('../../utils/httpclient.js')
VAR that = this
//httpclient.req(url, data, method, success, fail)
httpclient.req(
'http://localhost:8090/wxappservice/api/v1/wx/getSession',
{
apiName: 'WX_CODE',
code: this.data.wxcode
},
'GET',
function(result){
var thirdSessionId = result.data.data.sessionId;
that.setData({thirdSessionId: thirdSessionId})
//將thirdSessionId放入小程序緩存
wx.setStorageSync('thirdSessionId', thirdSessionId)
},
function(result){
console.log(result)
}
);
//httpclient.req(url, data, method, success, fail)
httpclient.req(
'http://localhost:8090/wxappservice/api/v1/wx/decodeUserInfo',
{
apiName: 'WX_DECODE_USERINFO',
encryptedData: this.data.encryptedData,
iv: this.data.iv,
sessionId: wx.getStorageSync('thirdSessionId')
},
'GET',
function(result){
//解密后的數據
console.log(result.data)
},
function(result){
console.log(result)
}
);
服務端解密實現(java)
/**
* 解密用戶敏感數據
* @param encryptedData 明文
* @param iv 加密算法的初始向量
* @param sessionId 會話ID
* @return
*/
@Api(name = ApiConstant.WX_DECODE_USERINFO)
@RequestMapping(value = "/api/v1/wx/decodeUserInfo", method = RequestMethod.GET, produces = "application/json")
public Map<String,Object> decodeUserInfo(@RequestParam(required = true,value = "encryptedData")String encryptedData,
@RequestParam(required = true,value = "iv")String iv,
@RequestParam(required = true,value = "sessionId")String sessionId){
//從緩存中獲取session_key
Object wxSessionObj = redisUtil.get(sessionId);
if(null == wxSessionObj){
return rtnParam(40008, null);
}
String wxSessionStr = (String)wxSessionObj;
String sessionKey = wxSessionStr.split("#")[0];
try {
AES aes = new AES();
byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv));
if(null != resultByte && resultByte.length > 0){
String userInfo = new String(resultByte, "UTF-8");
return rtnParam(0, userInfo);
}
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return rtnParam(50021, null);
}
AES解密核心代碼
public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
initialize();
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
Key sKeySpec = new SecretKeySpec(keyByte, "AES");
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
最后的效果如下:
解密數據有一個與官方文檔不一致的地方是實際解密得到的數據不包含unionId。
從解密的數據看,算得上敏感的數據只有appid;個人覺得openid不是敏感數據,每個用戶針對每個公眾號會產生一個安全的openid;openid只有在appid的作用域下可用。除非你的appid也泄露了。
那么可以從解密數據得到appid,微信小程序團隊是何用意呢?還是前面那句話,openid脫離了appid就什么都不是,openid和appid一起為了方便小程序開發者做到不同小程序應用之間用戶區分和隔離,同時能夠將微信用戶體系與第三方業務體系結合。
所以我認為敏感數據解密的主要用處不是解密后回傳給客戶端,而是在服務端將微信用戶信息融入到自身業務當中。
詳細學習請參考:
小程序數據解密:https://github.com/cocoli/weixin_smallexe/tree/master/chaptor_05
java解密實現:https://github.com/cocoli/springboot-weapp-demo
相關討論專題:http://www.mkhyf.com/topic/429