一、概述
微信推出了小程序,很多公司的客户端应用不仅具有了APP、H5、还接入了小程序开发。但是,小程序中竟然没有提供Java版本的加密数据解密算法。这着实让广大的Java开发人员蛋疼。
微信小程序提供的加密数据解密算法链接为:https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html
最新地址为:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
二、实小程序端的实现(小程序app.js)
doLogin(scene) {
wx.showLoading({
title: '加载中...',
})
var _this = this;
wx.login({
success: function (loginRes) {
if (loginRes) {
//获取用户信息
wx.getUserInfo({
success: function (infoRes) {
console.log('===开始request请求');
//请求服务端的登录接口
wx.request({
url: _this.globalData.url + '/v1/api/login',
data: {
code: loginRes.code,//临时登录凭证
rawData: infoRes.rawData,//用户非敏感信息
signature: infoRes.signature,//签名
encrypteData: infoRes.encryptedData,//用户敏感信息
iv: infoRes.iv,//解密算法的向量
scene: _this.globalData.scene
},
success: function (res) {
res = res.data;
wx.hideLoading();
if (res.code == 1) {
console.log('登录成功!');
wx.setStorageSync('userInfo', res.data);
wx.navigateBack({
delta: -1
});
} else {
console.log('登录失败!');
}
},
fail: function (error) {
//调用服务端登录接口失败
console.log(error);
}
});
}
});
} else {
}
}
});
}
三、实现Java版本的微信小程序加密数据解密算法
package com.api.controller.wx;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.codehaus.xfire.util.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.api.entity.User;
import com.api.service.UserService;
import com.api.utils.Constant;
import com.api.utils.HttpSenderUtil;
import com.api.utils.Result;
import com.api.utils.StringUtils;
@RequestMapping("/v1/api")
@Controller
public class WxLoginController {
@Autowired
private UserService userService;
@RequestMapping("/login")
@ResponseBody
public Result<Map<String,Object>> doLogin(Model model,
@RequestParam(value = "code",required = false) String code,
@RequestParam(value = "rawData",required = false) String rawData,
@RequestParam(value = "signature",required = false) String signature,
@RequestParam(value = "encrypteData",required = false) String encrypteData,
@RequestParam(value = "iv",required = false) String iv,
@RequestParam(value = "scene",required = false) String scene){
//System.out.println("用户非敏感信息"+rawData);
JSONObject rawDataJson = JSON.parseObject( rawData );
//System.out.println("签名"+signature);
JSONObject SessionKeyOpenId = getSessionKeyOrOpenId( code );
//System.out.println("SessionAndopenId="+SessionKeyOpenId);
String openId = SessionKeyOpenId.getString("openid" );
String sessionKey = SessionKeyOpenId.getString( "session_key" );
//System.out.println("openid="+openId+",session_key="+sessionKey);
Map<String,Object> mapUser = userService.findByOpenid(openId);
User user = new User();
if(mapUser==null){
String nickName = rawDataJson.getString( "nickName" );
String avatarUrl = rawDataJson.getString( "avatarUrl" );
String gender = rawDataJson.getString( "gender" );
String city = rawDataJson.getString( "city" );
String country = rawDataJson.getString( "country" );
String province = rawDataJson.getString( "province" );
user.setOpenId(openId);
user.setSessionKey(sessionKey);
user.setuName(nickName);
user.setuGender(gender);
user.setuAddress(country+" "+province+" "+city);
user.setuAvatar(avatarUrl);
user.setIsSys("0");
user.setActivityNum("3");
user.setuExtensionQr("");
user.setuExtensionId(scene);
user.setCreateTime(StringUtils.newDate());
user = userService.save( user );
}else {
System.out.println("用户已存在");
user.setuId(mapUser.get("U_ID").toString());
user.setIsSys(mapUser.get("IS_SYS").toString());
}
JSONObject userInfo = getUserInfo( encrypteData, sessionKey, iv );
userInfo.put("uId", user.getuId());
userInfo.put("isSys", user.getIsSys());
System.out.println("根据解密算法获取的userInfo="+userInfo);
return Result.success(userInfo);
}
public static JSONObject getSessionKeyOrOpenId(String code){
//微信端登录code
String wxCode = code;
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session?appid="+Constant.APP_ID+"&secret="+Constant.APP_SECRET+"&js_code="+wxCode+"&grant_type=authorization_code";
//发送post请求读取调用微信接口获取openid用户唯一标识
JSONObject jsonObject = JSON.parseObject( HttpSenderUtil.sendPost( requestUrl,null ));
return jsonObject;
}
public static JSONObject getUserInfo(String encryptedData,String sessionKey,String iv){
// 被加密的数据
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decode(sessionKey);
// 偏移量
byte[] ivByte = Base64.decode(iv);
try {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
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, "UTF-8");
return JSON.parseObject(result);
}
} catch (NoSuchAlgorithmException e) {
System.out.println(e.getMessage());
} catch (NoSuchPaddingException e) {
System.out.println(e.getMessage());
} catch (InvalidParameterSpecException e) {
System.out.println(e.getMessage());
} catch (IllegalBlockSizeException e) {
System.out.println(e.getMessage());
} catch (BadPaddingException e) {
System.out.println(e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println(e.getMessage());
} catch (InvalidKeyException e) {
System.out.println(e.getMessage());
} catch (InvalidAlgorithmParameterException e) {
System.out.println(e.getMessage());
} catch (NoSuchProviderException e) {
System.out.println(e.getMessage());
}
return null;
}
}
四、使用
将app.js中的代码复制进app.js,然后调用方法登录即可!
注意修改其中的链接以及appid和appsecret
补充:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.56</version>
</dependency>