最近在弄微信小程序,需要獲取到用戶的unionid等數據,網上有很多帖子,但是自己還是耗費了很長時間,下面分享下總流程及方法(借鑑了網上很多骨灰級大神的文章):
小程序代碼如下:
// 登錄
wx.login({
success: function (r) {
var code = r.code;//登錄憑證
if (code) {
//2、調用獲取用戶信息接口
wx.getUserInfo({
success: function (res) {
console.log({ encryptedData: res.encryptedData, iv: res.iv, code: code })
//3.解密用戶信息 獲取unionId
//...
//3.請求自己的服務器,解密用戶信息 獲取unionId等加密信息
wx.request({
url: '',//自己的服務接口地址
method: 'post',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: { encryptedData: res.encryptedData, iv: res.iv, code: code },
success: function (data) {
//4.解密成功後 獲取自己服務器返回的結果
if (data.data.status == 1) {
var userInfo_ = data.data.userInfo;
console.log(userInfo_)
} else {
console.log('解密失敗')
}
},
fail: function () {
console.log('系統錯誤')
}
})
},
fail: function () {
console.log('獲取用戶信息失敗')
}
})
} else {
console.log('獲取用戶登錄態失敗!' + r.errMsg)
}
},
fail: function () {
callback(false)
}
})
下面是java對稱解密的方法
1、首先是工具類
package com.tungus.utils;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Arrays;
public class AesUtil {
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 {
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;
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
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;
}
}
2、後臺接口
@ResponseBody
@RequestMapping(value = "/decodeUserInfo", method = RequestMethod.POST)
public Map decodeUserInfo(String encryptedData, String iv, String code) {
Map map = new HashMap();
//登錄憑證不能爲空
if (code == null || code.length() == 0) {
map.put("status", 0);
map.put("msg", "code 不能爲空");
return map;
}
//小程序唯一標識 (在微信小程序管理後臺獲取)
String wxspAppid = "";
//小程序的 app secret (在微信小程序管理後臺獲取)
String wxspSecret = "";
//授權(必填)
String grant_type = "authorization_code";
//////////////// 1、向微信服務器 使用登錄憑證 code 獲取 session_key 和 openid ////////////////
//請求參數
String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type=" + grant_type;
//發送請求
String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
//解析相應內容(轉換成json對象)
JSONObject json = JSONObject.parseObject(sr);
//獲取會話密鑰(session_key)
String session_key = json.get("session_key").toString();
//用戶的唯一標識(openid)
String openid = (String) json.get("openid");
//////////////// 2、對encryptedData加密數據進行AES解密 ////////////////
try {
String result = AesUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
if (null != result && result.length() > 0) {
map.put("status", 1);
map.put("msg", "解密成功");
JSONObject userInfoJSON = JSONObject.parseObject(result);
Map userInfo = new HashMap();
userInfo.put("openId", userInfoJSON.get("openId"));
userInfo.put("nickName", userInfoJSON.get("nickName"));
userInfo.put("gender", userInfoJSON.get("gender"));
userInfo.put("city", userInfoJSON.get("city"));
userInfo.put("province", userInfoJSON.get("province"));
userInfo.put("country", userInfoJSON.get("country"));
userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
userInfo.put("unionId", userInfoJSON.get("unionId"));
map.put("userInfo", userInfo);
return map;
}
} catch (Exception e) {
e.printStackTrace();
}
map.put("status", 0);
map.put("msg", "解密失敗");
return map;
}
重點來了:【在工具類中使用了第三方的加解密類庫】
一.引入三方加密庫類方法:
方法一:
(1)去BouncyCastle官網下載provider的包,然後放入$JAVA_HOME\jre\lib\ext目錄下;
(2)修改配置文件$JAVA_HOME\jre\lib\security\java.security,加入一行配置:security.provider.按順序填數字 =org.bouncycastle.jce.provider.BouncyCastleProvider
(3)代碼如下:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes("UTF-8"), "AES"));
cipher.doFinal("QWEASDZS".getBytes("UTF-8"));
方法二:
(1)在代碼中通過maven引入BouncyCastle的包【涉及到jdk版本,我使用的是jdk10,對應版本爲1.56】
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.56</version>
</dependency>
2)無需像方式一一樣修改配置文件,直接在代碼中手動添加provider:【工具類中的代碼就是】
二.引入對應的base64包
【參考鏈接:https://jingyan.baidu.com/article/597a06433601db312b52431c.html】
注意:傳參數進行測試時,三個參數每次都會改變。
小bug
【java.lang.NoClassDefFoundError: org/apache/commons/codec/binary/Base64】
解決方案:
加入驅動包
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
填坑【pad block corrupted】:
1.判斷三個參數是否傳輸正確
2.在工具類中添加【上面代碼中已做修改】
3.對key進行處理【上面代碼中已做修改】