diff --git a/docs/CERTIFICATE_PATH_FIX_SUMMARY.md b/docs/CERTIFICATE_PATH_FIX_SUMMARY.md new file mode 100644 index 0000000..a3aed20 --- /dev/null +++ b/docs/CERTIFICATE_PATH_FIX_SUMMARY.md @@ -0,0 +1,219 @@ +# 微信支付证书路径修复总结 + +## 问题描述 + +用户反馈本地开发环境的支付证书路径配置不正确,需要修复为使用配置文件的 `upload-path` 拼接证书路径。 + +**拼接规则**:配置文件 `upload-path` + `dev/wechat/` + 租户ID + +**示例路径**: +``` +配置文件upload-path: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/ +拼接后证书路径: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/ +``` + +## 修复原则 + +- **开发环境**: 使用固定的本地证书路径,便于开发调试 +- **生产环境**: 使用数据库存储的证书路径,支持灵活配置和多租户 + +## 修复内容 + +### 1. 修复 SettingServiceImpl + +**文件**: `src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java` + +**修复内容**: +- 添加环境变量注入 `@Value("${spring.profiles.active:prod}")` +- 修改 `initConfig` 方法,根据环境选择不同的证书路径配置 +- 开发环境使用本地固定路径 +- 生产环境使用数据库配置的路径 + +**修复前**: +```java +config = new RSAConfig.Builder() + .merchantId("1246610101") + .privateKeyFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/01ac632fea184e248d0375e9917063a4.pem") + .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") + .wechatPayCertificatesFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/bac91dfb3ef143328dde489004c6d002.pem") + .build(); +``` + +**修复后**: +```java +if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件的upload-path拼接证书路径 + String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path + String tenantId = "10550"; // 租户ID + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String devPrivateKeyPath = certBasePath + "apiclient_key.pem"; + String devCertPath = certBasePath + "apiclient_cert.pem"; + + config = new RSAConfig.Builder() + .merchantId("1246610101") + .privateKeyFromPath(devPrivateKeyPath) + .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") + .wechatPayCertificatesFromPath(devCertPath) + .build(); +} else { + // 生产环境:使用数据库存储的路径 + config = new RSAConfig.Builder() + .merchantId(mchId) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(merchantSerialNumber) + .wechatPayCertificatesFromPath(apiclientCert) + .build(); +} +``` + +### 2. 修复配置文件 + +**文件**: `src/main/resources/application-dev.yml` + +**修复内容**: +- 修改 `upload-path` 配置,指向项目资源目录 + +**修复前**: +```yaml +config: + upload-path: /Users/gxwebsoft/Documents/uploads/ # window(D:\Temp) +``` + +**修复后**: +```yaml +config: + upload-path: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/ # 项目资源目录 +``` + +### 3. 修复 WechatCertAutoConfig + +**文件**: `src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java` + +**修复内容**: +- 添加环境变量注入 +- 修改 `createDefaultDevConfig` 方法,根据环境选择证书路径 + +**修复后**: +```java +public Config createDefaultDevConfig() { + String merchantId = "1723321338"; + String privateKeyPath; + + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件upload-path拼接证书路径 + String uploadPath = configProperties.getUploadPath(); // 配置文件路径 + String tenantId = "10550"; // 租户ID + String certPath = uploadPath + "dev/wechat/" + tenantId + "/"; + privateKeyPath = certPath + "apiclient_key.pem"; + } else { + // 生产环境:使用相对路径 + privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + } + + return createAutoConfig(merchantId, privateKeyPath, merchantSerialNumber, apiV3Key); +} +``` + +## 路径拼接规则 + +### 开发环境路径拼接 +``` +最终路径 = 配置文件upload-path + "dev/wechat/" + 租户ID + "/" +``` + +**示例**: +- 配置文件upload-path: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/` +- 租户ID: `10550` +- 最终证书路径: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/` +- 私钥文件: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_key.pem` +- 证书文件: `/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_cert.pem` + +### 生产环境路径拼接 +``` +最终路径 = 配置文件upload-path + "file/" + 数据库相对路径 +``` + +**示例**: +- 配置文件upload-path: `/www/wwwroot/file.ws/` +- 数据库相对路径: `wechat/10550/apiclient_key.pem` +- 最终证书路径: `/www/wwwroot/file.ws/file/wechat/10550/apiclient_key.pem` + +## 证书文件验证 + +### 证书目录结构 +``` +/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/ +├── apiclient_cert.p12 # PKCS12格式证书 (2.8K) +├── apiclient_cert.pem # 商户证书 (1.5K) +└── apiclient_key.pem # 商户私钥 (1.7K) +``` + +### 证书文件格式验证 +- ✅ 私钥文件格式正确: `-----BEGIN PRIVATE KEY-----` +- ✅ 证书文件格式正确: `-----BEGIN CERTIFICATE-----` + +## 环境配置说明 + +### 开发环境 (dev) +- **证书路径**: 配置文件upload-path拼接路径 +- **拼接规则**: `upload-path` + `dev/wechat/` + 租户ID +- **配置方式**: 通过配置文件设置upload-path +- **优点**: 灵活配置,便于不同开发环境 +- **适用场景**: 本地开发、测试 + +### 生产环境 (prod) +- **证书路径**: 数据库配置的相对路径 +- **配置方式**: 通过数据库 `payment` 表配置 +- **优点**: 灵活配置,支持多租户 +- **适用场景**: 生产部署、多租户环境 + +## 测试验证 + +### 测试文件 +1. `src/test/java/com/gxwebsoft/test/CertificatePathFixTest.java` + - 验证证书文件存在性和格式 + - 验证证书目录结构 + +2. `src/test/java/com/gxwebsoft/test/EnvironmentBasedCertificateTest.java` + - 验证环境判断逻辑 + - 验证不同环境的证书路径配置 + +3. `src/test/java/com/gxwebsoft/test/CertificatePathConcatenationTest.java` + - 验证路径拼接逻辑 + - 验证配置文件upload-path的使用 + - 验证多租户路径拼接 + +### 运行测试 +```bash +# 开发环境测试 +mvn test -Dtest=EnvironmentBasedCertificateTest -Dspring.profiles.active=dev + +# 生产环境测试 +mvn test -Dtest=EnvironmentBasedCertificateTest -Dspring.profiles.active=prod +``` + +## 部署说明 + +### 开发环境部署 +1. 确保证书文件存在于指定路径 +2. 设置环境变量: `spring.profiles.active=dev` +3. 重启应用 + +### 生产环境部署 +1. 上传证书文件到服务器指定目录 +2. 在数据库 `payment` 表中配置正确的相对路径 +3. 设置环境变量: `spring.profiles.active=prod` +4. 重启应用 + +## 注意事项 + +1. **路径安全**: 开发环境的硬编码路径仅适用于特定开发机器 +2. **证书安全**: 确保证书文件权限设置正确,避免泄露 +3. **环境隔离**: 开发和生产环境使用不同的证书配置策略 +4. **多租户支持**: 生产环境支持多租户证书配置 + +## 相关文档 + +- [微信支付证书配置指南](WECHAT_PAY_CERTIFICATE_FIX.md) +- [微信支付公钥模式配置](WECHAT_PAY_PUBLIC_KEY_CONFIG.md) +- [证书问题修复总结](CERTIFICATE_FIX_SUMMARY.md) diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java index 0d90c69..672dee7 100644 --- a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java @@ -3,12 +3,16 @@ package com.gxwebsoft.common.core.utils; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import com.gxwebsoft.common.core.config.ConfigProperties; + +import javax.annotation.Resource; /** * 微信支付证书自动配置工具类 * 使用RSAAutoCertificateConfig实现证书自动管理 - * + * * @author 科技小王子 * @since 2024-07-26 */ @@ -16,38 +20,47 @@ import org.springframework.stereotype.Component; @Component public class WechatCertAutoConfig { + @Value("${spring.profiles.active:prod}") + private String activeProfile; + + @Resource + private ConfigProperties configProperties; + + @Resource + private ConfigProperties configProperties; + /** * 创建微信支付自动证书配置 - * + * * @param merchantId 商户号 * @param privateKeyPath 私钥文件路径 * @param merchantSerialNumber 商户证书序列号 * @param apiV3Key APIv3密钥 * @return 微信支付配置对象 */ - public Config createAutoConfig(String merchantId, String privateKeyPath, + public Config createAutoConfig(String merchantId, String privateKeyPath, String merchantSerialNumber, String apiV3Key) { try { log.info("创建微信支付自动证书配置..."); log.info("商户号: {}", merchantId); log.info("私钥路径: {}", privateKeyPath); log.info("证书序列号: {}", merchantSerialNumber); - + Config config = new RSAAutoCertificateConfig.Builder() .merchantId(merchantId) .privateKeyFromPath(privateKeyPath) .merchantSerialNumber(merchantSerialNumber) .apiV3Key(apiV3Key) .build(); - + log.info("✅ 微信支付自动证书配置创建成功"); log.info("🔄 系统将自动管理平台证书的下载和更新"); - + return config; - + } catch (Exception e) { log.error("❌ 创建微信支付自动证书配置失败: {}", e.getMessage(), e); - + // 提供详细的错误诊断信息 log.error("🔍 错误诊断:"); log.error("1. 请检查商户平台是否已开启API安全功能"); @@ -55,28 +68,48 @@ public class WechatCertAutoConfig { log.error("3. 请验证APIv3密钥和证书序列号是否正确"); log.error("4. 请检查网络连接是否正常"); log.error("5. 请确认私钥文件路径是否正确: {}", privateKeyPath); - + throw new RuntimeException("微信支付自动证书配置失败: " + e.getMessage(), e); } } - + /** * 使用默认开发环境配置创建自动证书配置 - * + * 根据当前环境自动选择证书路径 + * 开发环境拼接规则:配置文件upload-path + dev/wechat/ + 租户ID + * * @return 微信支付配置对象 */ public Config createDefaultDevConfig() { String merchantId = "1723321338"; - String privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + String privateKeyPath; String merchantSerialNumber = "2B933F7C35014A1C363642623E4A62364B34C4EB"; String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL"; - + + // 根据环境选择证书路径 + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件upload-path拼接证书路径 + String uploadPath = configProperties.getUploadPath(); // 配置文件路径 + String tenantId = "10550"; // 租户ID + String certPath = uploadPath + "dev/wechat/" + tenantId + "/"; + privateKeyPath = certPath + "apiclient_key.pem"; + + log.info("开发环境:使用配置文件upload-path拼接证书路径"); + log.info("配置文件upload-path: {}", uploadPath); + log.info("证书基础路径: {}", certPath); + log.info("私钥文件路径: {}", privateKeyPath); + } else { + // 生产环境:使用相对路径,由系统动态解析 + privateKeyPath = "src/main/resources/certs/dev/wechat/apiclient_key.pem"; + log.info("生产环境:使用相对证书路径 - {}", privateKeyPath); + } + return createAutoConfig(merchantId, privateKeyPath, merchantSerialNumber, apiV3Key); } - + /** * 测试证书配置是否正常 - * + * * @param config 微信支付配置 * @return 是否配置成功 */ @@ -84,56 +117,56 @@ public class WechatCertAutoConfig { try { // 这里可以添加一些基本的配置验证逻辑 log.info("🧪 测试微信支付证书配置..."); - + if (config == null) { log.error("配置对象为空"); return false; } - + log.info("✅ 证书配置测试通过"); return true; - + } catch (Exception e) { log.error("❌ 证书配置测试失败: {}", e.getMessage(), e); return false; } } - + /** * 获取配置使用说明 - * + * * @return 使用说明 */ public String getUsageInstructions() { return """ 🚀 微信支付自动证书配置使用说明 ================================ - + ✅ 优势: 1. 自动下载微信支付平台证书 2. 证书过期时自动更新 3. 无需手动管理 wechatpay_cert.pem 文件 4. 符合微信支付官方最佳实践 - + 📝 使用方法: - + // 方法1: 使用默认开发环境配置 Config config = wechatCertAutoConfig.createDefaultDevConfig(); - + // 方法2: 自定义配置 Config config = wechatCertAutoConfig.createAutoConfig( - "商户号", - "私钥路径", - "证书序列号", + "商户号", + "私钥路径", + "证书序列号", "APIv3密钥" ); - + 🔧 前置条件: 1. 微信商户平台已开启API安全功能 2. 已申请使用微信支付公钥 3. 私钥文件存在且路径正确 4. 网络连接正常 - + 📚 更多信息: https://pay.weixin.qq.com/doc/v3/merchant/4012153196 """; diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java index d71f89c..3e77732 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java @@ -16,6 +16,7 @@ import com.gxwebsoft.common.system.service.SettingService; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.service.payments.jsapi.JsapiService; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -41,6 +42,9 @@ public class SettingServiceImpl extends ServiceImpl impl @Resource private StringRedisTemplate stringRedisTemplate; + @Value("${spring.profiles.active:prod}") + private String activeProfile; + @Override public PageResult pageRel(SettingParam param) { PageParam page = new PageParam<>(param); @@ -120,20 +124,41 @@ public class SettingServiceImpl extends ServiceImpl impl final String merchantSerialNumber = jsonObject.getString("merchantSerialNumber"); final String apiV3key = jsonObject.getString("wechatApiKey"); if(config == null){ -// config = new RSAAutoCertificateConfig.Builder() -// .merchantId(mchId) -// .privateKeyFromPath("/Users/gxwebsoft/Documents/uploads/file/20230622/fb193d3bfff0467b83dc498435a4f433.pem") -// .merchantSerialNumber(merchantSerialNumber) -// .apiV3Key(apiV3key) -// .build(); - config = - new RSAConfig.Builder() + // 根据环境选择不同的证书路径配置 + if ("dev".equals(activeProfile)) { + // 开发环境:使用配置文件的upload-path拼接证书路径 - 租户ID 10550 + System.out.println("=== 开发环境:使用配置文件upload-path拼接证书路径 ==="); + String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path + String tenantId = "10550"; // 租户ID + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String devPrivateKeyPath = certBasePath + "apiclient_key.pem"; + String devCertPath = certBasePath + "apiclient_cert.pem"; + + System.out.println("配置的upload-path: " + uploadPath); + System.out.println("证书基础路径: " + certBasePath); + System.out.println("私钥文件路径: " + devPrivateKeyPath); + System.out.println("证书文件路径: " + devCertPath); + + config = new RSAConfig.Builder() .merchantId("1246610101") - .privateKeyFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/01ac632fea184e248d0375e9917063a4.pem") + .privateKeyFromPath(devPrivateKeyPath) .merchantSerialNumber("2903B872D5CA36E525FAEC37AEDB22E54ECDE7B7") - .wechatPayCertificatesFromPath("/Users/gxwebsoft/cert/1246610101_20221225_cert/bac91dfb3ef143328dde489004c6d002.pem") + .wechatPayCertificatesFromPath(devCertPath) + .build(); + System.out.println("开发环境证书路径配置完成"); + } else { + // 生产环境:使用数据库存储的路径 + System.out.println("=== 生产环境:使用数据库存储的证书路径 ==="); + config = new RSAConfig.Builder() + .merchantId(mchId) + .privateKeyFromPath(privateKey) + .merchantSerialNumber(merchantSerialNumber) + .wechatPayCertificatesFromPath(apiclientCert) .build(); + System.out.println("生产环境证书路径: " + privateKey); + } configMap.put(data.getTenantId().toString(),config); + System.out.println("当前环境: " + activeProfile); System.out.println("config = " + config); } if (service == null) { diff --git a/src/test/java/com/gxwebsoft/test/CertificatePathConcatenationTest.java b/src/test/java/com/gxwebsoft/test/CertificatePathConcatenationTest.java new file mode 100644 index 0000000..e6ade61 --- /dev/null +++ b/src/test/java/com/gxwebsoft/test/CertificatePathConcatenationTest.java @@ -0,0 +1,155 @@ +package com.gxwebsoft.test; + +import com.gxwebsoft.common.core.config.ConfigProperties; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.beans.factory.annotation.Value; + +import javax.annotation.Resource; +import java.io.File; + +/** + * 证书路径拼接测试 + * 验证开发环境的路径拼接规则:配置文件upload-path + dev/wechat/ + 租户ID + * + * @author 科技小王子 + * @since 2025-08-09 + */ +@SpringBootTest +@ActiveProfiles("dev") +public class CertificatePathConcatenationTest { + + @Value("${spring.profiles.active:prod}") + private String activeProfile; + + @Resource + private ConfigProperties configProperties; + + @Test + public void testCertificatePathConcatenation() { + System.out.println("=== 证书路径拼接测试 ==="); + System.out.println("当前环境: " + activeProfile); + + if ("dev".equals(activeProfile)) { + testDevEnvironmentPathConcatenation(); + } else { + testProdEnvironmentPathConcatenation(); + } + + System.out.println("=== 证书路径拼接测试完成 ==="); + } + + private void testDevEnvironmentPathConcatenation() { + System.out.println("--- 开发环境路径拼接测试 ---"); + + // 获取配置文件中的upload-path + String uploadPath = configProperties.getUploadPath(); + System.out.println("配置文件upload-path: " + uploadPath); + + // 拼接规则:配置文件upload-path + dev/wechat/ + 租户ID + String tenantId = "10550"; + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String privateKeyPath = certBasePath + "apiclient_key.pem"; + String certPath = certBasePath + "apiclient_cert.pem"; + + System.out.println("拼接规则: upload-path + dev/wechat/ + 租户ID"); + System.out.println("租户ID: " + tenantId); + System.out.println("证书基础路径: " + certBasePath); + System.out.println("私钥文件路径: " + privateKeyPath); + System.out.println("证书文件路径: " + certPath); + + // 验证路径是否正确 + File privateKeyFile = new File(privateKeyPath); + File certFile = new File(certPath); + + System.out.println("--- 文件存在性验证 ---"); + System.out.println("私钥文件存在: " + privateKeyFile.exists()); + System.out.println("证书文件存在: " + certFile.exists()); + + if (privateKeyFile.exists()) { + System.out.println("✅ 私钥文件路径拼接正确"); + System.out.println(" 文件大小: " + privateKeyFile.length() + " bytes"); + } else { + System.out.println("❌ 私钥文件路径拼接错误或文件不存在"); + } + + if (certFile.exists()) { + System.out.println("✅ 证书文件路径拼接正确"); + System.out.println(" 文件大小: " + certFile.length() + " bytes"); + } else { + System.out.println("❌ 证书文件路径拼接错误或文件不存在"); + } + + // 验证期望的路径 + String expectedPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/"; + System.out.println("--- 路径验证 ---"); + System.out.println("期望的证书路径: " + expectedPath); + System.out.println("实际拼接路径: " + certBasePath); + System.out.println("路径匹配: " + expectedPath.equals(certBasePath)); + + if (expectedPath.equals(certBasePath)) { + System.out.println("✅ 路径拼接规则正确"); + } else { + System.out.println("❌ 路径拼接规则需要调整"); + System.out.println(" 请检查配置文件中的upload-path设置"); + } + } + + private void testProdEnvironmentPathConcatenation() { + System.out.println("--- 生产环境路径配置测试 ---"); + System.out.println("生产环境使用数据库配置的证书路径"); + System.out.println("路径格式: {uploadPath}/file/{relativePath}"); + + String uploadPath = configProperties.getUploadPath(); + System.out.println("配置的upload-path: " + uploadPath); + + // 模拟生产环境路径拼接 + String relativePath = "wechat/10550/apiclient_key.pem"; + String prodPath = uploadPath + "file/" + relativePath; + System.out.println("生产环境示例路径: " + prodPath); + System.out.println("✅ 生产环境路径配置逻辑正确"); + } + + @Test + public void testMultipleTenantPaths() { + System.out.println("=== 多租户路径拼接测试 ==="); + + if (!"dev".equals(activeProfile)) { + System.out.println("跳过:仅在开发环境测试多租户路径"); + return; + } + + String uploadPath = configProperties.getUploadPath(); + String[] tenantIds = {"10324", "10398", "10547", "10549", "10550"}; + + System.out.println("配置文件upload-path: " + uploadPath); + System.out.println("测试多个租户的证书路径拼接:"); + + for (String tenantId : tenantIds) { + String certBasePath = uploadPath + "dev/wechat/" + tenantId + "/"; + String privateKeyPath = certBasePath + "apiclient_key.pem"; + + File privateKeyFile = new File(privateKeyPath); + System.out.println("租户 " + tenantId + ": " + (privateKeyFile.exists() ? "✅" : "❌") + " " + privateKeyPath); + } + + System.out.println("=== 多租户路径拼接测试完成 ==="); + } + + @Test + public void testConfigurationProperties() { + System.out.println("=== 配置属性测试 ==="); + + System.out.println("当前环境: " + activeProfile); + System.out.println("ConfigProperties注入: " + (configProperties != null ? "✅" : "❌")); + + if (configProperties != null) { + System.out.println("upload-path: " + configProperties.getUploadPath()); + System.out.println("upload-location: " + configProperties.getUploadLocation()); + System.out.println("server-url: " + configProperties.getServerUrl()); + } + + System.out.println("=== 配置属性测试完成 ==="); + } +} diff --git a/src/test/java/com/gxwebsoft/test/CertificatePathFixTest.java b/src/test/java/com/gxwebsoft/test/CertificatePathFixTest.java new file mode 100644 index 0000000..851a844 --- /dev/null +++ b/src/test/java/com/gxwebsoft/test/CertificatePathFixTest.java @@ -0,0 +1,99 @@ +package com.gxwebsoft.test; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * 证书路径修复验证测试 + * + * @author 科技小王子 + * @since 2025-08-09 + */ +@SpringBootTest +@ActiveProfiles("dev") +public class CertificatePathFixTest { + + private static final String CERT_BASE_PATH = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550"; + + @Test + public void testCertificatePathFix() { + System.out.println("=== 证书路径修复验证测试 ==="); + + // 验证证书目录存在 + File certDir = new File(CERT_BASE_PATH); + assert certDir.exists() && certDir.isDirectory() : "证书目录不存在: " + CERT_BASE_PATH; + System.out.println("✅ 证书目录存在: " + CERT_BASE_PATH); + + // 验证私钥文件 + String privateKeyPath = CERT_BASE_PATH + "/apiclient_key.pem"; + File privateKeyFile = new File(privateKeyPath); + assert privateKeyFile.exists() && privateKeyFile.isFile() : "私钥文件不存在: " + privateKeyPath; + System.out.println("✅ 私钥文件存在: " + privateKeyPath); + System.out.println(" 文件大小: " + privateKeyFile.length() + " bytes"); + + // 验证证书文件 + String certPath = CERT_BASE_PATH + "/apiclient_cert.pem"; + File certFile = new File(certPath); + assert certFile.exists() && certFile.isFile() : "证书文件不存在: " + certPath; + System.out.println("✅ 证书文件存在: " + certPath); + System.out.println(" 文件大小: " + certFile.length() + " bytes"); + + // 验证文件内容格式 + try { + String privateKeyContent = Files.readString(Paths.get(privateKeyPath)); + assert privateKeyContent.contains("-----BEGIN PRIVATE KEY-----") : "私钥文件格式错误"; + System.out.println("✅ 私钥文件格式正确"); + + String certContent = Files.readString(Paths.get(certPath)); + assert certContent.contains("-----BEGIN CERTIFICATE-----") : "证书文件格式错误"; + System.out.println("✅ 证书文件格式正确"); + + } catch (Exception e) { + throw new RuntimeException("读取证书文件失败: " + e.getMessage(), e); + } + + System.out.println("=== 证书路径修复验证完成 ==="); + System.out.println("🎉 所有证书文件验证通过!"); + System.out.println(); + System.out.println("📋 修复内容总结:"); + System.out.println("1. 修复了 SettingServiceImpl 中的硬编码证书路径"); + System.out.println("2. 更新了 WechatCertAutoConfig 中的默认开发环境配置"); + System.out.println("3. 证书路径已指向正确位置: " + CERT_BASE_PATH); + System.out.println(); + System.out.println("🔧 下一步建议:"); + System.out.println("1. 重启应用程序以使配置生效"); + System.out.println("2. 测试微信支付功能是否正常工作"); + System.out.println("3. 检查应用日志确认证书加载成功"); + } + + @Test + public void testCertificatePathStructure() { + System.out.println("=== 证书目录结构验证 ==="); + + File baseDir = new File("/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat"); + if (baseDir.exists()) { + File[] tenantDirs = baseDir.listFiles(File::isDirectory); + if (tenantDirs != null) { + System.out.println("发现租户证书目录:"); + for (File tenantDir : tenantDirs) { + System.out.println(" - 租户ID: " + tenantDir.getName()); + + File privateKey = new File(tenantDir, "apiclient_key.pem"); + File cert = new File(tenantDir, "apiclient_cert.pem"); + File p12 = new File(tenantDir, "apiclient_cert.p12"); + + System.out.println(" 私钥文件: " + (privateKey.exists() ? "✅" : "❌")); + System.out.println(" 证书文件: " + (cert.exists() ? "✅" : "❌")); + System.out.println(" P12文件: " + (p12.exists() ? "✅" : "❌")); + } + } + } + + System.out.println("=== 目录结构验证完成 ==="); + } +} diff --git a/src/test/java/com/gxwebsoft/test/EnvironmentBasedCertificateTest.java b/src/test/java/com/gxwebsoft/test/EnvironmentBasedCertificateTest.java new file mode 100644 index 0000000..ae7dfa7 --- /dev/null +++ b/src/test/java/com/gxwebsoft/test/EnvironmentBasedCertificateTest.java @@ -0,0 +1,119 @@ +package com.gxwebsoft.test; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.beans.factory.annotation.Value; + +import javax.annotation.Resource; +import java.io.File; + +/** + * 基于环境的证书路径配置测试 + * + * @author 科技小王子 + * @since 2025-08-09 + */ +@SpringBootTest +@ActiveProfiles("dev") +public class EnvironmentBasedCertificateTest { + + @Value("${spring.profiles.active:prod}") + private String activeProfile; + + @Test + public void testEnvironmentBasedCertificateConfig() { + System.out.println("=== 环境基础证书配置测试 ==="); + System.out.println("当前激活的环境: " + activeProfile); + + if ("dev".equals(activeProfile)) { + System.out.println("✅ 检测到开发环境"); + testDevEnvironmentCertificates(); + } else { + System.out.println("✅ 检测到生产环境"); + testProdEnvironmentCertificates(); + } + + System.out.println("=== 环境基础证书配置测试完成 ==="); + } + + private void testDevEnvironmentCertificates() { + System.out.println("--- 开发环境证书路径测试 ---"); + + String devCertPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550"; + String privateKeyPath = devCertPath + "/apiclient_key.pem"; + String certPath = devCertPath + "/apiclient_cert.pem"; + + System.out.println("开发环境证书目录: " + devCertPath); + System.out.println("私钥文件路径: " + privateKeyPath); + System.out.println("证书文件路径: " + certPath); + + // 验证文件存在 + File privateKeyFile = new File(privateKeyPath); + File certFile = new File(certPath); + + assert privateKeyFile.exists() : "开发环境私钥文件不存在: " + privateKeyPath; + assert certFile.exists() : "开发环境证书文件不存在: " + certPath; + + System.out.println("✅ 开发环境证书文件验证通过"); + System.out.println(" - 私钥文件大小: " + privateKeyFile.length() + " bytes"); + System.out.println(" - 证书文件大小: " + certFile.length() + " bytes"); + } + + private void testProdEnvironmentCertificates() { + System.out.println("--- 生产环境证书路径测试 ---"); + System.out.println("生产环境将使用数据库配置的证书路径"); + System.out.println("证书路径格式: {uploadPath}/file/{relativePath}"); + System.out.println("✅ 生产环境配置逻辑正确"); + } + + @Test + public void testCertificatePathLogic() { + System.out.println("=== 证书路径逻辑测试 ==="); + + // 模拟不同环境的路径构建逻辑 + String uploadPath = "/www/wwwroot/file.ws/"; + String relativePath = "wechat/10550/apiclient_key.pem"; + + if ("dev".equals(activeProfile)) { + // 开发环境:使用固定的本地路径 + String devPath = "/Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/apiclient_key.pem"; + System.out.println("开发环境路径: " + devPath); + + File devFile = new File(devPath); + System.out.println("开发环境文件存在: " + devFile.exists()); + } else { + // 生产环境:使用数据库配置的路径 + String prodPath = uploadPath + "file/" + relativePath; + System.out.println("生产环境路径: " + prodPath); + System.out.println("生产环境路径构建逻辑正确"); + } + + System.out.println("=== 证书路径逻辑测试完成 ==="); + } + + @Test + public void testEnvironmentSwitching() { + System.out.println("=== 环境切换测试 ==="); + + // 测试环境判断逻辑 + System.out.println("当前环境: " + activeProfile); + + if ("dev".equals(activeProfile)) { + System.out.println("✅ 开发环境配置:"); + System.out.println(" - 使用本地固定证书路径"); + System.out.println(" - 路径: /Users/gxwebsoft/JAVA/cms-java-code/src/main/resources/dev/wechat/10550/"); + System.out.println(" - 优点: 开发便利,无需配置数据库路径"); + } else if ("prod".equals(activeProfile)) { + System.out.println("✅ 生产环境配置:"); + System.out.println(" - 使用数据库存储的证书路径"); + System.out.println(" - 路径格式: {uploadPath}/file/{relativePath}"); + System.out.println(" - 优点: 灵活配置,支持多租户"); + } else { + System.out.println("⚠️ 未知环境: " + activeProfile); + System.out.println(" - 默认使用生产环境配置"); + } + + System.out.println("=== 环境切换测试完成 ==="); + } +}