微信登录具体参考:https://blog.csdn.net/weixin_42346767/article/details/102508736
1. 准备解密必须的jar包或导入相关依赖
2. 创建解密的API或工具类
3. 调用解析
1. 准备解密必须的jar包或导入相关依赖
// maven 的pom依赖
org.bouncycastle
bcprov-jdk16
1.46
2. 创建解密的API或工具类
package com.magic.nzoth.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
/**
* @program:
* @description: 微信用户加密信息解密工具
* @author: mingqi
* @create: 2019-10-11 15:53
**/
@Slf4j
public class AesCbcUtil {
// static {
// // BouncyCastle是一个开源的加解密解决方案
// Security.addProvider(new BouncyCastleProvider());
// }
// 固定编码
private static final String encodingFormat = "UTF-8";
/**
* 对外提供服务
* @return
*/
public static String decodeUserInfo(String data, String key, String iv){
return decodeBase64(data, key, iv);
}
/**
* AES解密
*
* @param data // 密文,被加密的数据
* @param key // 秘钥
* @param iv // 偏移量
* // * @param encodingFormat // 解密后的结果需要进行的编码
* @return
*/
private static String decodeBase64(String data, String key, String iv) {
// 被加密的数据
byte[] dataByte = Base64.decodeBase64(data);
// 加密秘钥
byte[] keyByte = Base64.decodeBase64(key);
// 偏移量
byte[] ivByte = Base64.decodeBase64(iv);
// BouncyCastle是一个开源的加解密解决方案
Security.addProvider(new BouncyCastleProvider());
try {
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
// Cipher cipher = Cipher.getInstance("BC");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPadding");
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, encodingFormat);
return result;
}
return null;
} catch (Exception e) {
log.info("解析失败:{}", e);
}
return null;
}
}
3. 调用解析
/**
* 解密微信返回的用户加密数据
*
* @param sessionKey
* @param iv
* @param encryptedData
*/
private JSONObject decodeUserData(String sessionKey, String iv, String encryptedData) {
JSONObject decodeData;
try {
log.info("解密数据:{},初始向量:{}, sessino_key:{}", encryptedData, iv, sessionKey);
String userdata = AesCbcUtil.decodeUserInfo(encryptedData, sessionKey, iv);
decodeData = JSON.parseObject(userdata);
log.info("解密后数据:{}", decodeData);
if (!ObjectUtils.isEmpty(decodeData)) {
return decodeData;
}
} catch (Exception e) {
log.info("微信解密失败:{}", e);
}
return null;
}
4. 解析后的数据应用
try {
// code2Session 接口未返回unionid字段,表明用户未关注公众号,同时需要特殊处理获取该字段
JSONObject decodeData = decodeUserData(session_key, iv, encryptedData);
if (!ObjectUtils.isEmpty(decodeData)) {
userInfo = decodeUserData(session_key, iv, encryptedData);
unionId = userInfo.getString("unionId");
} else {
unionId = "微信用户数据解析失败";
}
} catch (Exception e) {
log.info("解析获取用户数据报错:{}", e.getClass());
}
最开始解析工具类中Security.addProvider(new BouncyCastleProvider());是使用静态代码块初始加载的
// 报错:
javax.crypto.BadPaddingException: pad block corrupted
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.magic.nzoth.util.AesCbcUtil.decrypt(AesCbcUtil.java:63)
// 解析工具类配置:
static {
Security.addProvider(new BouncyCastleProvider());
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
网上给出的结局方案是AES 可以正常加解密,但是多用户并发操作时,有时会出现以上错误!
工具类中 将 cipher 定义为静态常量了,cipher 不是单例模式,改为调用时生成加载的方式
参考自:https://blog.csdn.net/yszd2017/article/details/78422608
// 解析工具类配置:
// BouncyCastle是一个开源的加解密解决方案
// 改行代码放到具体的方法中进行调用,而非静态代码块
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
报错:
com.alibaba.fastjson.JSONException: syntax error, pos 1, json : �ޜ�nX���V��f�g"_��{3[��ê�l���\��͗�j��T��+Q�4�U�����p�+�-��#�ƆIA�zf9�}�"�W���
��:���t".u˘B�`'I�2x6K�,8�Y�o��������M���$v��}�2�
解密后的数据存在乱码情况,想起了最开始捣鼓的时候依稀记得有说需要使用BC解密方式的,
// 解析工具类配置:
// BouncyCastle是一个开源的加解密解决方案
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("BC");
// 报错:
java.security.NoSuchAlgorithmException: Cannot find any provider supporting BC
at javax.crypto.Cipher.getInstance(Cipher.java:539)
at com.magic.nzoth.util.AesCbcUtil.decrypt(AesCbcUtil.java:53)
haha ~ ,尴尬了,找不到BC解密方式的 支持
// 解析工具类配置:
// BouncyCastle是一个开源的加解密解决方案
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/NOPadding");
// 最开始的AES/CBC/PKCS7Padding 配置到目前的 AES/CBC/NOPadding
// 可以解决报错,但是偶尔会乱码
// 成功情况
解密后数据:{"country":"China","unionId":"unionId","watermark":{"appid":"appid","timestamp":1570783572},"gender":1,"province":"Beijing","city":"Haidian","avatarUrl":"avatarUrl","openId":"openId","nickName":"有点过分,但是","language":"zh_CN"}
// 异常情况
���a� ?��E�@�D�pXH���)��p�PK`s����x+%�Z��M��Dlgٹq��n4_����/��0�j����d@MN���NA�������O���s��^���U�����\�w�
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1436)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1322)
at com.alibaba.fastjson.JSON.parse(JSON.java:152)
at com.alibaba.fastjson.JSON.parse(JSON.java:162)
at com.alibaba.fastjson.JSON.parse(JSON.java:131)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:223)
// 目前的现状是一次成功,一次失败
在网上看到一篇说是因为前端在调用wx.login 和 wx.getUserInfo 的接口顺序不一致会导致最开始的错误,反正我是不认同的,顺序有影响,但不应该是这种报错。评论中骂声一片。不过本文情形四采用的是评论中以为博主的发言
最后:前文中的工具类是我目前最终的工具类配置,解密不是特别稳定,偶尔解析失败,乱码,请做好异常处理机制。