mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-01-22 21:02:03 +08:00
🎨 #3841 【微信支付】修复 RsaCryptoUtil 无法加密继承字段和嵌套对象的问题
This commit is contained in:
@@ -14,8 +14,11 @@ import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 微信支付敏感信息加密
|
||||
@@ -36,10 +39,26 @@ public class RsaCryptoUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取类的所有字段,包括父类中的字段
|
||||
*
|
||||
* @param clazz 要获取字段的类
|
||||
* @return 所有字段的列表
|
||||
*/
|
||||
private static List<Field> getAllFields(Class<?> clazz) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
while (clazz != null && clazz != Object.class) {
|
||||
Field[] declaredFields = clazz.getDeclaredFields();
|
||||
Collections.addAll(fields, declaredFields);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static void encryptField(Object encryptObject, X509Certificate certificate) throws IllegalAccessException, IllegalBlockSizeException {
|
||||
Class<?> infoClass = encryptObject.getClass();
|
||||
Field[] infoFieldArray = infoClass.getDeclaredFields();
|
||||
for (Field field : infoFieldArray) {
|
||||
List<Field> infoFieldList = getAllFields(infoClass);
|
||||
for (Field field : infoFieldList) {
|
||||
if (field.isAnnotationPresent(SpecEncrypt.class)) {
|
||||
//字段使用了@SpecEncrypt进行标识
|
||||
if (field.getType().getTypeName().equals(JAVA_LANG_STRING)) {
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.github.binarywang.wxpay.v3.util;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingReceiverV3Request;
|
||||
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.v3.SpecEncrypt;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* RsaCryptoUtil 测试类
|
||||
*/
|
||||
public class RsaCryptoUtilTest {
|
||||
|
||||
/**
|
||||
* 测试反射能否找到嵌套类中的 @SpecEncrypt 注解字段
|
||||
*/
|
||||
@Test
|
||||
public void testFindAnnotatedFieldsInNestedClass() {
|
||||
// 创建 Receiver 对象
|
||||
ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver();
|
||||
receiver.setName("测试姓名");
|
||||
|
||||
// 使用反射查找带有 @SpecEncrypt 注解的字段
|
||||
Class<?> receiverClass = receiver.getClass();
|
||||
Field[] fields = receiverClass.getDeclaredFields();
|
||||
|
||||
boolean foundNameField = false;
|
||||
boolean nameFieldHasAnnotation = false;
|
||||
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals("name")) {
|
||||
foundNameField = true;
|
||||
if (field.isAnnotationPresent(SpecEncrypt.class)) {
|
||||
nameFieldHasAnnotation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证能够找到 name 字段并且它有 @SpecEncrypt 注解
|
||||
assertTrue(foundNameField, "应该能找到 name 字段");
|
||||
assertTrue(nameFieldHasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试嵌套对象中的字段加密
|
||||
* 验证 List<Receiver> 中每个 Receiver 对象的 name 字段是否能被正确找到和处理
|
||||
*/
|
||||
@Test
|
||||
public void testEncryptFieldsWithNestedObjects() {
|
||||
// 创建测试对象
|
||||
ProfitSharingV3Request request = ProfitSharingV3Request.newBuilder()
|
||||
.appid("test-appid")
|
||||
.subMchId("test-submchid")
|
||||
.transactionId("test-transaction")
|
||||
.outOrderNo("test-order-no")
|
||||
.unfreezeUnsplit(true)
|
||||
.build();
|
||||
|
||||
List<ProfitSharingV3Request.Receiver> receivers = new ArrayList<>();
|
||||
ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver();
|
||||
receiver.setName("张三"); // 设置需要加密的字段
|
||||
receiver.setAccount("test-account");
|
||||
receiver.setType("PERSONAL_OPENID");
|
||||
receiver.setAmount(100);
|
||||
receiver.setRelationType("STORE");
|
||||
receiver.setDescription("测试分账");
|
||||
|
||||
receivers.add(receiver);
|
||||
request.setReceivers(receivers);
|
||||
|
||||
// 验证 receivers 字段有 @SpecEncrypt 注解
|
||||
try {
|
||||
Field receiversField = ProfitSharingV3Request.class.getDeclaredField("receivers");
|
||||
boolean hasAnnotation = receiversField.isAnnotationPresent(SpecEncrypt.class);
|
||||
assertTrue(hasAnnotation, "receivers 字段应该有 @SpecEncrypt 注解");
|
||||
} catch (NoSuchFieldException e) {
|
||||
fail("应该能找到 receivers 字段");
|
||||
}
|
||||
|
||||
// 验证name字段不为null
|
||||
assertNotNull(receiver.getName());
|
||||
assertEquals(receiver.getName(), "张三");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试单个对象中的字段加密
|
||||
* 验证直接在对象上的 @SpecEncrypt 字段是否能被正确找到
|
||||
*/
|
||||
@Test
|
||||
public void testEncryptFieldsWithDirectField() {
|
||||
// 创建测试对象
|
||||
ProfitSharingReceiverV3Request request = ProfitSharingReceiverV3Request.newBuilder()
|
||||
.appid("test-appid")
|
||||
.subMchId("test-submchid")
|
||||
.type("PERSONAL_OPENID")
|
||||
.account("test-account")
|
||||
.name("李四")
|
||||
.relationType("STORE")
|
||||
.build();
|
||||
|
||||
// 验证 name 字段有 @SpecEncrypt 注解
|
||||
try {
|
||||
Field nameField = ProfitSharingReceiverV3Request.class.getDeclaredField("name");
|
||||
boolean hasAnnotation = nameField.isAnnotationPresent(SpecEncrypt.class);
|
||||
assertTrue(hasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
|
||||
} catch (NoSuchFieldException e) {
|
||||
fail("应该能找到 name 字段");
|
||||
}
|
||||
|
||||
// 验证name字段不为null
|
||||
assertNotNull(request.getName());
|
||||
assertEquals(request.getName(), "李四");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试类继承场景下的字段加密
|
||||
* 验证父类中带 @SpecEncrypt 注解的字段是否能被正确找到和加密
|
||||
*/
|
||||
@Test
|
||||
public void testEncryptFieldsWithInheritance() {
|
||||
// 定义测试用的父类和子类
|
||||
@Data
|
||||
class ParentRequest {
|
||||
@SpecEncrypt
|
||||
@SerializedName("parent_name")
|
||||
private String parentName;
|
||||
}
|
||||
|
||||
@Data
|
||||
@lombok.EqualsAndHashCode(callSuper = false)
|
||||
class ChildRequest extends ParentRequest {
|
||||
@SpecEncrypt
|
||||
@SerializedName("child_name")
|
||||
private String childName;
|
||||
|
||||
@Override
|
||||
protected boolean canEqual(final Object other) {
|
||||
return other instanceof ChildRequest;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建子类实例
|
||||
ChildRequest request = new ChildRequest();
|
||||
request.setParentName("父类字段");
|
||||
request.setChildName("子类字段");
|
||||
|
||||
// 验证能够找到父类和子类的字段
|
||||
// 使用 getDeclaredFields 只能找到子类字段
|
||||
Field[] childFields = ChildRequest.class.getDeclaredFields();
|
||||
|
||||
// 使用反射调用 RsaCryptoUtil 的私有 getAllFields 方法
|
||||
int annotatedFieldCount = 0;
|
||||
try {
|
||||
java.lang.reflect.Method getAllFieldsMethod = RsaCryptoUtil.class.getDeclaredMethod("getAllFields", Class.class);
|
||||
getAllFieldsMethod.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Field> allFields = (List<Field>) getAllFieldsMethod.invoke(null, ChildRequest.class);
|
||||
|
||||
for (Field field : allFields) {
|
||||
if (field.isAnnotationPresent(SpecEncrypt.class)) {
|
||||
annotatedFieldCount++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fail("无法调用 getAllFields 方法: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 应该找到2个带注解的字段(parentName 和 childName)
|
||||
assertTrue(annotatedFieldCount >= 2, "应该能找到至少2个带 @SpecEncrypt 注解的字段");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user