微信小程序解密*获取*unionid*java*
微信小程序获取unionId必须:
1.*注册微信开放平台,绑定需要获取unionid的应用。
2.*只是通过https://api.weixin.qq.com/sns/jscode2session
是永远拿不到unionId的只能拿session_key和openid,虽然官方说能拿到。
获取的流程:
*客户端(小程序)
wx.login->code
wx.userInfo->iv(偏移量),encryptedData(加密数据)传给后台。
---------------------------------------------------------------------
Java服务端:
1.必须的依赖,用于解密过程。(或使用mvn加载)
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.59' // https://mvnrepository.com/artifact/commons-codec/commons-codec compile group: 'commons-codec', name: 'commons-codec', version: '1.11' // https://mvnrepository.com/artifact/net.sf.json-lib/json-lib compile group: 'net.sf.json-lib', name: 'json-lib', version: '2.4'
AES工具类
package yourpkg; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; 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.apache.ofbiz.base.util.Debug; public class AES { public static boolean initialized = false; public final static String module = AES.class.getName(); /** * AES解密 * * @param content * 密文 * @return * @throws InvalidAlgorithmParameterException * @throws NoSuchProviderException */ public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException { initialize(); try { Debug.logInfo("content:"+content,module); Debug.logInfo("keyByte:"+keyByte,module); Debug.logInfo("ivByte:"+ivByte,module); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); Debug.logInfo("cipher:"+cipher,module); Key sKeySpec = new SecretKeySpec(keyByte, "AES"); Debug.logInfo("sKeySpec:"+sKeySpec,module); cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化 byte[] result = cipher.doFinal(content); Debug.logInfo("result:"+result,module); return result; } catch (NoSuchAlgorithmException e) { Debug.logInfo("NoSuchAlgorithmException",module); e.printStackTrace(); } catch (NoSuchPaddingException e) { Debug.logInfo("NoSuchPaddingException",module); e.printStackTrace(); } catch (InvalidKeyException e) { Debug.logInfo("InvalidKeyException",module); e.printStackTrace(); } catch (IllegalBlockSizeException e) { Debug.logInfo("IllegalBlockSizeException",module); e.printStackTrace(); } catch (BadPaddingException e) { Debug.logInfo("BadPaddingException",module); e.printStackTrace(); } catch (NoSuchProviderException e) { Debug.logInfo("NoSuchProviderException",module); // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { Debug.logInfo("Exception",module); // 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; } }
WX工具类
import org.apache.commons.codec.binary.Base64; import net.sf.json.JSONObject; import org.apache.ofbiz.base.util.Debug; import org.apache.commons.codec.binary.Base64; public class WXUtil { /** * 解密数据 * @return * @throws Exception */ public static String decrypt(String appId, String encryptedData, String sessionKey, String iv){ String result = null; try { AES aes = new AES(); byte[] resultByte = aes.decrypt(Base64.decodeBase64(encryptedData), Base64.decodeBase64(sessionKey), Base64.decodeBase64(iv)); if(null != resultByte && resultByte.length > 0){ result = new String(WxPKCS7Encoder.decode(resultByte)); JSONObject jsonObject = JSONObject.fromObject(result); return result; } } catch (Exception e) { result = ""; e.printStackTrace(); } return null; } }
解密工具类
import java.nio.charset.Charset; import java.util.Arrays; public class WxPKCS7Encoder { private static final Charset CHARSET = Charset.forName("utf-8"); private static final int BLOCK_SIZE = 32; /** * 获得对明文进行补位填充的字节. * * @param count * 需要进行填充补位操作的明文字节个数 * @return 补齐用的字节数组 */ public static byte[] encode(int count) { // 计算需要填充的位数 int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); if (amountToPad == 0) { amountToPad = BLOCK_SIZE; } // 获得补位所用的字符 char padChr = chr(amountToPad); String tmp = new String(); for (int index = 0; index < amountToPad; index++) { tmp += padChr; } return tmp.getBytes(CHARSET); } /** * 删除解密后明文的补位字符 * * @param decrypted * 解密后的明文 * @return 删除补位字符后的明文 */ public static byte[] decode(byte[] decrypted) { int pad = decrypted[decrypted.length - 1]; if (pad < 1 || pad > 32) { pad = 0; } return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); } /** * 将数字转化成ASCII码对应的字符,用于对明文进行补码 * * @param a * 需要转化的数字 * @return 转化得到的字符 */ public static char chr(int a) { byte target = (byte) (a & 0xFF); return (char) target; } }
先通过code拿session_key,这是解密的关键
WECHAT_MINI_PROGRAM_SNS_PATH:
https://api.weixin.qq.com/sns/jscode2session
StringBuffer requestParamSB = new StringBuffer(); requestParamSB.append("appid="+appId);//AK requestParamSB.append("&secret="+sc);//SC requestParamSB.append("&js_code="+code);//用户微信授权代码 requestParamSB.append("&grant_type=authorization_code");//授予类型 String responseStr = HttpRequestHelper.sendGet(SysConstant.WECHAT_MINI_PROGRAM_SNS_PATH, requestParamSB.toString());
HttpRequestHelper类
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HTTP; import net.sf.json.JSONObject; public class HttpRequestHelper { /** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection connection = realUrl.openConnection(); // 设置通用的请求属性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立实际的连接 connection.connect(); // 获取所有响应头字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义 BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader( connection.getInputStream(),"utf-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } }
然后将这些值传入方法即可,appId可以不要,只是起到校验作用。
WXUtil.decrypt(appId,encryptedData,sessionKey,iv);
微信反馈结果格式:
{"openId":"ofql75ZdllClk7lnuc4","nickName":"Xxx","gender":1,"language":"zh_CN","city":"new york","province":"ina","country":"gogx","avatarUrl":"https://wx.qlogo.cn/mmopen/vi_a2/Q0j4TwGTfTJgWxRlibYMHfVdjCDqibj8lfTch1hwzl2CvcmficSvS2sZh1icXrQhwJlCFS4LwknHd3F7FWfnxg/1d2","unionId":"opDl4wlzx_QrxyN4vxm1A","watermark":{"timestamp":154222947163,"appid":"wxc3asdr3v5b32b3531f3a"}}
注意:
当你无法解密或者解密出的结果为空时,(或者出现padblockcorrupted报错)
请友善提示前端同学,可能是他的问题。
参考:微信小程序解密encryptedData报错:padblockcorrupted解决方法https://blog.csdn.net/l523115401/article/details/80251859