Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
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;

/**
* 微信支付敏感信息加密
Expand All @@ -36,10 +39,26 @@ public static void encryptFields(Object encryptObject, X509Certificate certifica
}
}

/**
* 递归获取类的所有字段,包括父类中的字段
*
* @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();
java.util.Collections.addAll(fields, declaredFields);
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在第52行,应该直接使用 Collections.addAll(fields, declaredFields) 而不是使用完全限定名 java.util.Collections.addAll(fields, declaredFields)。因为已经在第20行导入了 java.util.Collections,使用完全限定名是多余的,不符合代码风格。

Suggested change
java.util.Collections.addAll(fields, declaredFields);
Collections.addAll(fields, declaredFields);

Copilot uses AI. Check for mistakes.
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)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
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();

System.out.println("=== Receiver 类中的所有字段 ===");
boolean foundNameField = false;
boolean nameFieldHasAnnotation = false;

for (Field field : fields) {
System.out.println("字段名: " + field.getName() + ", 类型: " + field.getType().getName());
if (field.getName().equals("name")) {
foundNameField = true;
if (field.isAnnotationPresent(SpecEncrypt.class)) {
nameFieldHasAnnotation = true;
System.out.println(" -> name 字段有 @SpecEncrypt 注解");
} else {
System.out.println(" -> name 字段没有 @SpecEncrypt 注解");
}
}
}
Comment on lines +35 to +50
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在测试代码中使用 System.out.println 进行调试输出不是最佳实践。建议移除这些调试输出语句,或者使用日志框架(如 SLF4J)进行日志记录。保留的调试输出会在测试运行时产生不必要的控制台输出。

Copilot uses AI. Check for mistakes.

// 验证能够找到 name 字段并且它有 @SpecEncrypt 注解
assertTrue(foundNameField, "应该能找到 name 字段");
assertTrue(nameFieldHasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
}

/**
* 测试嵌套对象中的字段加密
* 验证 List<Receiver> 中每个 Receiver 对象的 name 字段是否能被正确找到和处理
*/
@Test
public void testEncryptFieldsWithNestedObjects() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个测试目前只验证注解存在/字段赋值,并未实际调用 RsaCryptoUtil.encryptFields() 或断言字段已被加密,因此即使修复逻辑回退测试也可能仍然通过(同类问题也出现在本文件的其他测试方法中)。

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

// 创建测试对象
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);
System.out.println("ProfitSharingV3Request.receivers 字段有 @SpecEncrypt 注解: " + hasAnnotation);
assertTrue(hasAnnotation, "receivers 字段应该有 @SpecEncrypt 注解");
} catch (NoSuchFieldException e) {
fail("应该能找到 receivers 字段");
}

System.out.println("测试对象创建成功,name字段: " + receiver.getName());
Comment on lines +88 to +94
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在测试代码中使用 System.out.println 进行调试输出不是最佳实践。建议移除这些调试输出语句,或者使用日志框架(如 SLF4J)进行日志记录。保留的调试输出会在测试运行时产生不必要的控制台输出。

Copilot uses AI. Check for mistakes.
// 验证name字段不为null
assertNotNull(receiver.getName());
assertEquals(receiver.getName(), "张三");
}
Comment on lines +61 to +98
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试方法 testEncryptFieldsWithNestedObjectstestEncryptFieldsWithDirectField 没有实际调用 RsaCryptoUtil.encryptFields() 方法来验证加密功能是否正常工作。这些测试只验证了字段存在性和注解存在性,但没有测试核心的加密逻辑。建议添加实际的加密调用和验证,确保修复后的方法能够正确加密嵌套对象中的字段。

Copilot uses AI. Check for mistakes.

/**
* 测试单个对象中的字段加密
* 验证直接在对象上的 @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);
System.out.println("ProfitSharingReceiverV3Request.name 字段有 @SpecEncrypt 注解: " + hasAnnotation);
assertTrue(hasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
} catch (NoSuchFieldException e) {
fail("应该能找到 name 字段");
}

System.out.println("测试对象创建成功,name字段: " + request.getName());
Comment on lines +120 to +126
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在测试代码中使用 System.out.println 进行调试输出不是最佳实践。建议移除这些调试输出语句,或者使用日志框架(如 SLF4J)进行日志记录。保留的调试输出会在测试运行时产生不必要的控制台输出。

Suggested change
System.out.println("ProfitSharingReceiverV3Request.name 字段有 @SpecEncrypt 注解: " + hasAnnotation);
assertTrue(hasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
} catch (NoSuchFieldException e) {
fail("应该能找到 name 字段");
}
System.out.println("测试对象创建成功,name字段: " + request.getName());
assertTrue(hasAnnotation, "name 字段应该有 @SpecEncrypt 注解");
} catch (NoSuchFieldException e) {
fail("应该能找到 name 字段");
}

Copilot uses AI. Check for mistakes.
// 验证name字段不为null
assertNotNull(request.getName());
assertEquals(request.getName(), "李四");
}
Comment on lines +104 to +130
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试方法 testEncryptFieldsWithDirectField 没有实际调用 RsaCryptoUtil.encryptFields() 方法来验证加密功能是否正常工作。此测试只验证了字段存在性和注解存在性,但没有测试核心的加密逻辑。建议添加实际的加密调用和验证,确保修复后的方法能够正确加密字段。

Copilot uses AI. Check for mistakes.

/**
* 测试类继承场景下的字段加密
* 验证父类中带 @SpecEncrypt 注解的字段是否能被正确找到和加密
*/
@Test
public void testEncryptFieldsWithInheritance() {
// 定义测试用的父类和子类
@Data
class ParentRequest {
@SpecEncrypt
@SerializedName("parent_name")
private String parentName;
}

@Data
@lombok.EqualsAndHashCode(callSuper = false)
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method overrides ParentRequest.canEqual; it is advisable to add an Override annotation.

Copilot uses AI. Check for mistakes.
class ChildRequest extends ParentRequest {
@SpecEncrypt
@SerializedName("child_name")
private String childName;
}

// 创建子类实例
ChildRequest request = new ChildRequest();
request.setParentName("父类字段");
request.setChildName("子类字段");

// 验证能够找到父类和子类的字段
System.out.println("=== 测试继承场景 ===");
System.out.println("父类字段值: " + request.getParentName());
System.out.println("子类字段值: " + request.getChildName());

// 使用 getDeclaredFields 只能找到子类字段
Field[] childFields = ChildRequest.class.getDeclaredFields();
System.out.println("使用 getDeclaredFields 找到的字段数: " + childFields.length);

// 使用 getAllFields 辅助方法应该能找到父类和子类的所有字段
List<Field> allFields = getAllFields(ChildRequest.class);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里调用的是测试类内自定义的 getAllFields(),并没有覆盖到生产代码里新增的字段遍历逻辑与 encryptField 路径,继承场景的回归保护会偏弱。

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎

System.out.println("使用 getAllFields 找到的字段数: " + allFields.size());

int annotatedFieldCount = 0;
for (Field field : allFields) {
if (field.isAnnotationPresent(SpecEncrypt.class)) {
annotatedFieldCount++;
System.out.println(" -> 找到带 @SpecEncrypt 注解的字段: " + field.getName());
Comment on lines +160 to +176
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在测试代码中使用 System.out.println 进行调试输出不是最佳实践。建议移除这些调试输出语句,或者使用日志框架(如 SLF4J)进行日志记录。保留的调试输出会在测试运行时产生不必要的控制台输出。

Suggested change
System.out.println("=== 测试继承场景 ===");
System.out.println("父类字段值: " + request.getParentName());
System.out.println("子类字段值: " + request.getChildName());
// 使用 getDeclaredFields 只能找到子类字段
Field[] childFields = ChildRequest.class.getDeclaredFields();
System.out.println("使用 getDeclaredFields 找到的字段数: " + childFields.length);
// 使用 getAllFields 辅助方法应该能找到父类和子类的所有字段
List<Field> allFields = getAllFields(ChildRequest.class);
System.out.println("使用 getAllFields 找到的字段数: " + allFields.size());
int annotatedFieldCount = 0;
for (Field field : allFields) {
if (field.isAnnotationPresent(SpecEncrypt.class)) {
annotatedFieldCount++;
System.out.println(" -> 找到带 @SpecEncrypt 注解的字段: " + field.getName());
// 使用 getDeclaredFields 只能找到子类字段
Field[] childFields = ChildRequest.class.getDeclaredFields();
// 使用 getAllFields 辅助方法应该能找到父类和子类的所有字段
List<Field> allFields = getAllFields(ChildRequest.class);
int annotatedFieldCount = 0;
for (Field field : allFields) {
if (field.isAnnotationPresent(SpecEncrypt.class)) {
annotatedFieldCount++;

Copilot uses AI. Check for mistakes.
}
}

// 应该找到2个带注解的字段(parentName 和 childName)
assertTrue(annotatedFieldCount >= 2, "应该能找到至少2个带 @SpecEncrypt 注解的字段");
}

/**
* 辅助方法:递归获取类的所有字段,包括父类中的字段
*/
private List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null && clazz != Object.class) {
Field[] declaredFields = clazz.getDeclaredFields();
java.util.Collections.addAll(fields, declaredFields);
clazz = clazz.getSuperclass();
}
return fields;
}
Comment on lines +187 to +195
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

辅助方法 getAllFields 在测试类中重复实现了与生产代码中相同的逻辑。这会导致代码重复,如果将来需要修改这个逻辑,需要在两个地方同时修改。建议直接使用反射调用 RsaCryptoUtil 中的私有 getAllFields 方法,或者将该方法改为包级别可见性以便测试使用。

Copilot uses AI. Check for mistakes.
}
Loading