解密流程
代码实现
小程序客户端
wx.login({
// 获取code
success: function (res) {
//用拿到的code,从后台获取sessionId
wx.request({
url: sessionID,
method: 'GET',
data: {
js_code: res.code,
movieCode: movieCode
},
header: {
"Content-Type": "application/x-www-form-urlencoded",
'Accept': 'application/json'
},
success: function (res) {
//从后台获取到的sessionId
var sessionId = res.data.resultData;
wx.setStorageSync('sessionId', sessionId);
//获取加密数据encryptedData
wx.getUserInfo({
success: function(msg){
var encryptedData = msg.encryptedData,
iv = msg.iv;
//发起解密请求
wx.request({
url: userInfoUrl,
method: 'POST',
data: {
encryptedData: encryptedData,
iv: iv,
sessionId: sessionId
},
header: {
"Content-Type": "application/x-www-form-urlencoded",
'Accept': 'application/json'
},
success: function (res) {
console.log(res, 1234);
var data = JSON.parse(res.data.resultData),
resultCode = res.data.resultCode,
resultDesc = res.data.resultDesc;
}
})
}
});
}
});
服务端(Java)
- 服务端获取sessionId
/**
* 初始化微信小程序参数
* @param request
* @param js_code 登录时获取的 code
* @return
*/
@ResponseBody
@RequestMapping("/initWxLogin")
public Result initWxLogin(HttpServletRequest request,
@RequestParam(value="js_code",required = true)String js_code,
@RequestParam(value="movieCode",required = true)String movieCode){
logger.info("开始执行请求初始化微信小程序参数:/initWxLogin");
Result result = new Result();
Map<String,String> params = new HashMap();
MovieCompany company = new MovieCompany();
company.setCompanyCode(movieCode);
List<MovieCompany> companies = movieCompanyService.getCompanyInfo(company);
CinemaPay cinemaPay = cinemaPayService.queryCinemaPayByCompanyCode(movieCode,"5");
if(companies == null || companies.size() == 0){
result.setResultDesc("院线信息不正确");
result.setResultCode(Constants.Result_Error_Code_500);
return result;
}
params.put("appid", cinemaPay.getWechatAppId());
params.put("secret",cinemaPay.getWechatDecodeAppSecret());
params.put("js_code",js_code);
params.put("grant_type","authorization_code");
String resultData = HttpUtils.doPost(wxLoginUrl, params, "UTF-8");
Map<String,Object> data = JsonMapper.getInstance().fromJson(resultData,Map.class);
String cacheKey = IdUtils.getRandomByUUId();
String openid ;
if(data!=null){
if(null!=data.get("openid")) { //正常返回的JSON数据包
openid = data.get("openid").toString();
String sessionKey = data.get("session_key").toString();
String val = sessionKey+"#"+openid;
result.setResultData(cacheKey);
redis.set(cacheKey,val,60*5);
} else {
result.setResultData(resultData);
result.setResultCode(Constants.Result_Error_Code_500);
}
}
return result;
}
- 解密实现
/**
* 解密小程序用户敏感数据
* @param encryptedData 明文
* @param iv 加密算法的初始向量
* @param sessionId 会话ID
* @return
*/
@ResponseBody
@RequestMapping(value = "/decodeUserInfo")
public Result decodeUserInfo(@RequestParam(required = true,value = "encryptedData")String encryptedData,
@RequestParam(required = true,value = "iv")String iv,
@RequestParam(required = true,value = "sessionId")String sessionId){
logger.info("开始执行请求解密小程序用户敏感数据:/decodeUserInfo");
Result result= new Result();
//从缓存中获取session_key
String wxSessionObj = redis.get(sessionId,String.class);
if(StringUtils.isEmpty(wxSessionObj)){
result.setResultCode(Constants.Result_Error_Code_500);
result.setResultDesc("sessionId不存在");
return result;
}
String wxSessionStr = (String)wxSessionObj;
String sessionKey = wxSessionStr.split("#")[0];
try {
AESUtils aes = new AESUtils();
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");
result.setResultData(userInfo);
return result;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
result.setResultCode(Constants.Result_Error_Code_500);
result.setResultDesc("获取用户解密信息失败");
} catch (InvalidAlgorithmParameterException e) {
result.setResultCode(Constants.Result_Error_Code_500);
result.setResultDesc("获取用户解密信息失败");
e.printStackTrace();
}
return result;
}
- AES解密
public static boolean initialized = false;
/**
* AES解密
* @param content 密文
* @return
* @throws InvalidAlgorithmParameterException
* @throws NoSuchProviderException
*/
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();
}
return null;
}
public static void initialize(){
if (initialized) return;
Security.addProvider(new BouncyCastleProvider());
initialized = true;
}
//生成iv
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv));
return params;
}
结语
总体思路就是,小程序客户端调用微信服务器接口,获取code、加密数据encryptedData,以及偏移向量iv;然后将code发送到开发者服务器,开发者服务器通过code换取秘钥session_key和openId,并返回到开发者服务器,然后开发者服务器生成sessonId,以sessionId为key,session_key和openId为value,存入缓存,并将sessionId返回到小程序客户端。然后小程序客户端将sessionId、wncrypteddata、iv发送到开发者服务器进行解密。