Bladeren bron

refactor(util): 替换AesCryptoUtil为AESCryptoUtils统一加密工具

- 删除旧的AesCryptoUtil类,使用新的AESCryptoUtils替代- 更新ChargingUtil和ElectricTokenManager中的加密解密调用
- 保留并启用原被注释的AES加解密核心逻辑
- 引入ConnectivityConstants常量支持平台密钥配置
- 完善参数校验和异常处理机制
-优化随机密钥与初始化向量生成方法
- 添加完整的加解密测试用例验证功能正确性
wzq 4 weken geleden
bovenliggende
commit
aa1013af9e

+ 6 - 0
pom.xml

@@ -69,6 +69,12 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+
+
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>

+ 13 - 0
src/main/java/com/zsElectric/boot/charging/controller/ChargingBusinessController.java

@@ -5,6 +5,7 @@ import com.zsElectric.boot.charging.vo.ChargingPricePolicyVO;
 import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO;
 import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
 import com.zsElectric.boot.charging.vo.QueryStationsInfoVO;
+import com.zsElectric.boot.common.util.electric.ApiToken;
 import com.zsElectric.boot.core.web.Result;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -26,6 +27,18 @@ public class ChargingBusinessController {
 
     private final ChargingBusinessService chargingBusinessService;
 
+    /**
+     * <p>获取token</p>
+     * @author wzq
+     * @return 获取到的token
+     *
+     */
+    @Operation(summary = "获取token")
+    @GetMapping("/queryToken")
+    public Result<ApiToken> queryToken(){
+        return Result.success(chargingBusinessService.queryToken());
+    }
+
     /**
      * <p>查询业务策略信息</p>
      * @author SheepHy

+ 7 - 0
src/main/java/com/zsElectric/boot/charging/service/ChargingBusinessService.java

@@ -4,11 +4,18 @@ import com.zsElectric.boot.charging.vo.ChargingPricePolicyVO;
 import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO;
 import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
 import com.zsElectric.boot.charging.vo.QueryStationsInfoVO;
+import com.zsElectric.boot.common.util.electric.ApiToken;
+import com.zsElectric.boot.common.util.electric.QueryTokenResponseData;
+import org.springframework.stereotype.Service;
 
 import java.util.List;
 
+
+@Service
 public interface ChargingBusinessService {
 
+    ApiToken queryToken();
+
     /**
      * <p>查询业务策略信息</p>
      * @author SheepHy

+ 73 - 4
src/main/java/com/zsElectric/boot/charging/service/impl/ChargingBusinessServiceImpl.java

@@ -9,14 +9,19 @@ import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO;
 import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
 import com.zsElectric.boot.charging.vo.QueryStationsInfoVO;
 import com.zsElectric.boot.common.constant.ConnectivityConstants;
-import com.zsElectric.boot.common.util.electric.ChargingUtil;
+import com.zsElectric.boot.common.util.AESCryptoUtils;
+import com.zsElectric.boot.common.util.HmacMD5Util;
+import com.zsElectric.boot.common.util.SequenceGenUtil;
+import com.zsElectric.boot.common.util.electric.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 
 @Slf4j
@@ -27,7 +32,68 @@ public class ChargingBusinessServiceImpl implements ChargingBusinessService {
     private final ChargingUtil chargingUtil;
 
     @Override
-    public ChargingPricePolicyVO queryEquipBusinessPolicy(String equipBizSeq, String connectorID){
+    public ApiToken queryToken() {
+        try {
+            QueryTokenParms queryTokenParms = new QueryTokenParms();
+            queryTokenParms
+                    .setOperatorID(ConnectivityConstants.OPERATOR_ID)
+                    .setOperatorSecret(ConnectivityConstants.PLATFORM_OPERATOR_SECRET);
+
+            JsonObject response = chargingUtil.chargingRequest(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_TOKEN, BeanUtil.beanToMap(queryTokenParms), false);
+            ResponseParmsEntity responseParms = new Gson().fromJson(response, ResponseParmsEntity.class);
+            String data = responseParms.getRet() + responseParms.getMsg() + responseParms.getData();
+            boolean verify = HmacMD5Util.verify(data, ConnectivityConstants.PLATFORM_SIG_SECRET, responseParms.getSig());
+            if (!verify) {
+                log.error("第三方接口响应数据签名验证失败");
+                return null;
+            }
+            if (responseParms.getRet() != 0) {
+                switch (responseParms.getRet()) {
+                    case -1:
+                        throw new RuntimeException("系统繁忙,此时请求方稍后重试");
+                    case 4001:
+                        throw new RuntimeException("签名错误");
+                    case 4002:
+                        throw new RuntimeException("Token错误");
+                    case 4003:
+                        throw new RuntimeException("参数不合法,缺少必需的示例:OperatorID、Sig、TimeStamp、Data、Seq五个参数");
+                    case 4004:
+                        throw new RuntimeException("请求的业务参数不合法,各接口定义自己的必须参数");
+                    case 500:
+                        throw new RuntimeException("系统错误");
+                }
+            }
+            String decodeData = AESCryptoUtils.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
+                    ConnectivityConstants.PLATFORM_DATA_SECRET_IV);
+
+            QueryTokenResponseData responseData = new Gson().fromJson(decodeData, QueryTokenResponseData.class);
+
+            if (responseData.getSuccStat() == 1) {
+                //0-无,1-OperatorID无效
+                String failReason = "";
+                if (responseData.getFailReason() == 0) {
+                    failReason = "无";
+                }
+                if (responseData.getFailReason() == 1) {
+                    failReason = "OperatorID无效";
+                }
+                log.error("调用第三方接口获取Token失败,失败原因: {}", failReason);
+                return null;
+            }
+            ApiToken apiToken = new ApiToken();
+            apiToken.setAccessToken(responseData.getAccessToken());
+            apiToken.setTokenAvailableTime(responseData.getTokenAvailableTime());
+            apiToken.setObtainTime(LocalDateTime.now());
+            apiToken.setExpireTime(apiToken.getObtainTime().plusSeconds(apiToken.getTokenAvailableTime()));
+
+            return apiToken;
+        } catch (Exception e) {
+            throw new RuntimeException("调用第三方接口失败", e);
+        }
+    }
+
+    @Override
+    public ChargingPricePolicyVO queryEquipBusinessPolicy(String equipBizSeq, String connectorID) {
         Map<String, Object> queryTokenParms = new HashMap<>();
         queryTokenParms.put("equipBizSeq", equipBizSeq);
         queryTokenParms.put("connectorID", connectorID);
@@ -53,7 +119,7 @@ public class ChargingBusinessServiceImpl implements ChargingBusinessService {
     }
 
     @Override
-    public QueryStationsInfoVO queryStationsInfo(String LastQueryTime, Integer PageNo, Integer PageSize){
+    public QueryStationsInfoVO queryStationsInfo(String LastQueryTime, Integer PageNo, Integer PageSize) {
         Map<String, Object> queryParms = new HashMap<>();
         queryParms.put("LastQueryTime", LastQueryTime);
         queryParms.put("PageNo", PageNo);
@@ -63,11 +129,14 @@ public class ChargingBusinessServiceImpl implements ChargingBusinessService {
         log.info("查询充电站信息返回结果:{}", jsonObject);
         JsonObject responseDecode = chargingUtil.responseDecode(jsonObject);
         log.info("查询充电站信息返回结果解密后:{}", responseDecode);
+        if (responseDecode == null) {
+            return null;
+        }
         return new Gson().fromJson(responseDecode, QueryStationsInfoVO.class);
     }
 
     @Override
-    public QueryStationStatusVO queryStationStatus(List<String> stationIDs){
+    public QueryStationStatusVO queryStationStatus(List<String> stationIDs) {
         Map<String, Object> queryParms = new HashMap<>();
         queryParms.put("StationIDs", stationIDs);
         log.info("设备接口状态查询请求参数:{}", queryParms);

+ 171 - 182
src/main/java/com/zsElectric/boot/common/util/AESCryptoUtils.java

@@ -1,5 +1,9 @@
 package com.zsElectric.boot.common.util;
 
+import com.esotericsoftware.minlog.Log;
+import com.zsElectric.boot.common.constant.ConnectivityConstants;
+import lombok.extern.slf4j.Slf4j;
+
 import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
@@ -13,194 +17,179 @@ import static com.zsElectric.boot.common.constant.ConnectivityConstants.*;
  * 支持AES-128-CBC-PKCS5Padding对称加解密算法
  * @version 1.0
  */
+@Slf4j
 public class AESCryptoUtils {
     
-//    // 加密算法/模式/填充方式
-//    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
-//    private static final String AES = "AES";
-//    private static final int IV_SIZE = 16;   // 16字节
-//
-//    /**
-//     * AES加密
-//     * @param data 待加密的明文
-//     * @param key 密钥(必须为16字节)
-//     * @param iv 初始化向量(必须为16字节)
-//     * @return Base64编码的加密结果
-//     * @throws Exception 加密异常
-//     */
-//    public static String encrypt(String data, String key, String iv) throws Exception {
-//        // 参数校验
-//        if (data == null || data.isEmpty()) {
-//            throw new IllegalArgumentException("加密数据不能为空");
-//        }
-//        if (key == null || key.length() != 16) {
-//            throw new IllegalArgumentException("密钥必须为16位字符");
-//        }
-//        if (iv == null || iv.length() != IV_SIZE) {
-//            throw new IllegalArgumentException("初始化向量必须为16位字符");
-//        }
-//
-//        try {
-//            // 创建密钥规范
-//            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
-//            // 创建初始化向量规范
-//            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
-//
-//            // 获取加密实例并初始化
-//            Cipher cipher = Cipher.getInstance(ALGORITHM);
-//            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
-//
-//            // 执行加密
-//            byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
-//
-//            // 返回Base64编码的加密结果
-//            return Base64.getEncoder().encodeToString(encryptedBytes);
-//
-//        } catch (Exception e) {
-//            throw new Exception("AES加密失败: " + e.getMessage(), e);
-//        }
-//    }
-//
-//    /**
-//     * AES解密
-//     * @param encryptedData Base64编码的加密数据
-//     * @param key 密钥(必须为16字节)
-//     * @param iv 初始化向量(必须为16字节)
-//     * @return 解密后的明文
-//     * @throws Exception 解密异常
-//     */
-//    public static String decrypt(String encryptedData, String key, String iv) throws Exception {
-//        // 参数校验
-//        if (encryptedData == null || encryptedData.isEmpty()) {
-//            throw new IllegalArgumentException("解密数据不能为空");
-//        }
-//        if (key == null || key.length() != 16) {
-//            throw new IllegalArgumentException("密钥必须为16位字符");
-//        }
-//        if (iv == null || iv.length() != 16) {
-//            throw new IllegalArgumentException("初始化向量必须为16位字符");
-//        }
-//
-//        try {
-//            // 创建密钥规范
-//            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
-//            // 创建初始化向量规范
-//            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
-//
-//            // 获取解密实例并初始化
-//            Cipher cipher = Cipher.getInstance(ALGORITHM);
-//            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
-//
-//            // Base64解码并执行解密
-//            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
-//            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
-//
-//            // 返回解密结果
-//            return new String(decryptedBytes, StandardCharsets.UTF_8);
-//
-//        } catch (Exception e) {
-//            throw new Exception("AES解密失败: " + e.getMessage(), e);
-//        }
-//    }
-//
-//    /**
-//     * 生成随机密钥(16字节)
-//     * @return 16字节的随机密钥
-//     */
-//    public static String generateRandomKey() {
-//        return generateRandomString(16);
-//    }
-//
-//    /**
-//     * 生成随机初始化向量(16字节)
-//     * @return 16字节的随机IV
-//     */
-//    public static String generateRandomIV() {
-//        return generateRandomString(16);
-//    }
-//
-//    /**
-//     * 生成指定长度的随机字符串
-//     * @param length 字符串长度
-//     * @return 随机字符串
-//     */
-//    private static String generateRandomString(int length) {
-//        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-//        StringBuilder sb = new StringBuilder(length);
-//        for (int i = 0; i < length; i++) {
-//            int index = (int) (characters.length() * Math.random());
-//            sb.append(characters.charAt(index));
-//        }
-//        return sb.toString();
-//    }
+    // 加密算法/模式/填充方式
+    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
+    private static final String AES = "AES";
+    private static final int IV_SIZE = 16;   // 16字节
+
+    /**
+     * AES加密
+     * @param data 待加密的明文
+     * @param key 密钥(必须为16字节)
+     * @param iv 初始化向量(必须为16字节)
+     * @return Base64编码的加密结果
+     * @throws Exception 加密异常
+     */
+    public static String encrypt(String data, String key, String iv) throws Exception {
+
+        log.info("待加密数据:{}", data);
+
+        // 参数校验
+        if (data == null || data.isEmpty()) {
+            throw new IllegalArgumentException("加密数据不能为空");
+        }
+        if (key == null || key.length() != 16) {
+            throw new IllegalArgumentException("密钥必须为16位字符");
+        }
+        if (iv == null || iv.length() != IV_SIZE) {
+            throw new IllegalArgumentException("初始化向量必须为16位字符");
+        }
+
+        try {
+            // 创建密钥规范
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
+            // 创建初始化向量规范
+            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+
+            // 获取加密实例并初始化
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+
+            // 执行加密
+            byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
+
+            // 返回Base64编码的加密结果
+            return Base64.getEncoder().encodeToString(encryptedBytes);
+
+        } catch (Exception e) {
+            throw new Exception("AES加密失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * AES解密
+     * @param encryptedData Base64编码的加密数据
+     * @param key 密钥(必须为16字节)
+     * @param iv 初始化向量(必须为16字节)
+     * @return 解密后的明文
+     * @throws Exception 解密异常
+     */
+    public static String decrypt(String encryptedData, String key, String iv) throws Exception {
+        // 参数校验
+        if (encryptedData == null || encryptedData.isEmpty()) {
+            throw new IllegalArgumentException("解密数据不能为空");
+        }
+        if (key == null || key.length() != 16) {
+            throw new IllegalArgumentException("密钥必须为16位字符");
+        }
+        if (iv == null || iv.length() != 16) {
+            throw new IllegalArgumentException("初始化向量必须为16位字符");
+        }
+
+        try {
+            // 创建密钥规范
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
+            // 创建初始化向量规范
+            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
+
+            // 获取解密实例并初始化
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
+
+            // Base64解码并执行解密
+            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
+            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+            // 返回解密结果
+            return new String(decryptedBytes, StandardCharsets.UTF_8);
+
+        } catch (Exception e) {
+            throw new Exception("AES解密失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 生成随机密钥(16字节)
+     * @return 16字节的随机密钥
+     */
+    public static String generateRandomKey() {
+        return generateRandomString(16);
+    }
+
+    /**
+     * 生成随机初始化向量(16字节)
+     * @return 16字节的随机IV
+     */
+    public static String generateRandomIV() {
+        return generateRandomString(16);
+    }
+
+    /**
+     * 生成指定长度的随机字符串
+     * @param length 字符串长度
+     * @return 随机字符串
+     */
+    private static String generateRandomString(int length) {
+        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        StringBuilder sb = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            int index = (int) (characters.length() * Math.random());
+            sb.append(characters.charAt(index));
+        }
+        return sb.toString();
+    }
     
     /**
      * 测试方法
      */
     public static void main(String[] args) throws Exception {
-//        String decryptedData = decrypt("B3eJFJNOlN7ubFMoxqAxVg==",
-//                DATA_SECRET,
-//                DATA_SECRET_IV);
-//        System.out.println("解密结果: " + decryptedData);
-//        try {
-//            // 测试数据
-//            String originalData = "{\n" +
-//                    "    \"total\": 1,\n" +
-//                    "    \"stationStatusInfo\": {\n" +
-//                    "        \"operationlD\": \"123456789\",\n" +
-//                    "        \"stationlD\": \"111111111111111\",\n" +
-//                    "        \"connectorStatusInfos\":{\"connectorD\":1,\"equipmentD\":\"10000000000000000000001\",\n" +
-//                    "        \"status\":4,\n" +
-//                    "        \"curentA\":0,\n" +
-//                    "        \"currentB\":0,\n" +
-//                    "        \"curentC\":0,\n" +
-//                    "        \"voltageA\":0,\n" +
-//                    "        \"voltageB\":0,\n" +
-//                    "        \"voltageC\":0,\n" +
-//                    "        \"soc\":10\n" +
-//                    "        }\n" +
-//                    "    }\n" +
-//                    "}";
-//            String key = "1234567890abcdef";   // 16字节密钥
-//            String iv = "1234567890abcdef";   // 16字节初始化向量
-//
-//            System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
-//            System.out.println("原始数据: " + originalData);
-//            System.out.println("密钥: " + key);
-//            System.out.println("初始化向量: " + iv);
-//
-//            // 加密
-//            long startTime = System.currentTimeMillis();
-//            String encryptedData = encrypt(originalData, key, iv);
-//            long encryptTime = System.currentTimeMillis() - startTime;
-//            System.out.println("加密结果: " + encryptedData);
-//            System.out.println("加密耗时: " + encryptTime + "ms");
-//
-//            // 解密
-//            startTime = System.currentTimeMillis();
-//            String decryptedData = decrypt(encryptedData, key, iv);
-//            long decryptTime = System.currentTimeMillis() - startTime;
-//            System.out.println("解密结果: " + decryptedData);
-//            System.out.println("解密耗时: " + decryptTime + "ms");
-//
-//            // 验证加解密一致性
-//            boolean isSuccess = originalData.equals(decryptedData);
-//            System.out.println("加解密验证: " + (isSuccess ? "成功" : "失败"));
-//
-//            // 测试随机密钥生成
-//            System.out.println("\n=== 随机密钥生成测试 ===");
-//            String randomKey = generateRandomKey();
-//            String randomIV = generateRandomIV();
-//            System.out.println("随机密钥: " + randomKey);
-//            System.out.println("随机IV: " + randomIV);
-//
-//            // 使用随机密钥进行加解密测试
-//            String testEncrypted = encrypt("测试数据", randomKey, randomIV);
-//            String testDecrypted = decrypt(testEncrypted, randomKey, randomIV);
-//            System.out.println("随机密钥加解密测试: " + ("测试数据".equals(testDecrypted) ? "成功" : "失败"));
-//
-//        } catch (Exception e) {
-//            e.printStackTrace();
-//        }
+
+        try {
+            // 测试数据
+            String originalData = "{\"OperatorID\":\"MAA9A6L75\", \"OperatorSecret\":\"yY8GtZrjhHcwptSZ\"}";
+            String key = PLATFORM_DATA_SECRET;   // 16字节密钥
+            String iv = PLATFORM_DATA_SECRET_IV;   // 16字节初始化向量
+
+            System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
+            System.out.println("原始数据: " + originalData);
+            System.out.println("密钥: " + key);
+            System.out.println("初始化向量: " + iv);
+
+            // 加密
+            long startTime = System.currentTimeMillis();
+            String encryptedData = encrypt(originalData, key, iv);
+            long encryptTime = System.currentTimeMillis() - startTime;
+            System.out.println("加密结果: " + encryptedData);
+            System.out.println("加密耗时: " + encryptTime + "ms");
+
+            // 解密
+            startTime = System.currentTimeMillis();
+            String decryptedData = decrypt(encryptedData, key, iv);
+            long decryptTime = System.currentTimeMillis() - startTime;
+            System.out.println("解密结果: " + decryptedData);
+            System.out.println("解密耗时: " + decryptTime + "ms");
+
+            // 验证加解密一致性
+            boolean isSuccess = originalData.equals(decryptedData);
+            System.out.println("加解密验证: " + (isSuccess ? "成功" : "失败"));
+
+            // 测试随机密钥生成
+            System.out.println("\n=== 随机密钥生成测试 ===");
+            String randomKey = generateRandomKey();
+            String randomIV = generateRandomIV();
+            System.out.println("随机密钥: " + randomKey);
+            System.out.println("随机IV: " + randomIV);
+
+            // 使用随机密钥进行加解密测试
+            String testEncrypted = encrypt("测试数据", randomKey, randomIV);
+            String testDecrypted = decrypt(testEncrypted, randomKey, randomIV);
+            System.out.println("随机密钥加解密测试: " + ("测试数据".equals(testDecrypted) ? "成功" : "失败"));
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
     }
 }

+ 3 - 3
src/main/java/com/zsElectric/boot/common/util/HmacMD5Util.java

@@ -33,7 +33,7 @@ public class HmacMD5Util {
             String seq,
             String sigSecret) throws NoSuchAlgorithmException {
 
-        String content = (operatorId + data + timeStamp + seq).toUpperCase();
+        String content = (operatorId + data + timeStamp + seq);
         return hmacMD5Hex(content, sigSecret);
     }
     
@@ -197,8 +197,8 @@ public class HmacMD5Util {
     public static void main(String[] args) {
         try {
             // 测试数据
-            String data = "z5rjHHzpZLxYPWO1F8mM9ylLOlllU8w/qB8+RCpmKkA9F9kTzJTNlpeDlXKIL/LpX/6MMFTLfeYIGgzckepOBOZnk5GEb3LsoxprtFFm8ipUMpIBiMldJ5wH0YrMHsMu2zfGwMOI2XNcrCCwgGTTeTqugOFxilC2QDQ7HAFDl0AylUp5WiddJ+Pi7fNC5R+lNf6AaivwpMcq+24Ax0iWFxxhyuKRE+Zk5hDwCEyKmzb2ZWav0cJD0L+v63vjbq6KmyM6NE5QafFKHyQ1Xlm3+jeBNhU4ufkinPWMgw0NEPajPxeE4wp+9YEt8p+fXjPmDr2aGKkdRWUzVslWhZIxPWrhCNrQcas7S5G2hIViw2+Wosxkt52kGTPAnIcTfSVQ0zjUQI45Ywgu4PqZklNT9Uh0d2ktnoL+kpuOLF4NeBy9WTLIGB2WNscBYYPnXZuN6kIc+ML026Z7manQC35qTluemcQ3reMhxWiw+owIbMo/0+kUek2MeHPLK9m512QUUpJDoJU1tAkPNvCiqkBBUg==";
-            String key = "1234567890abcdef";
+            String data = "MAA9A6L75Gpw3qXJ39TZp2zcW4wtvpw4Fq9VM8TNmsxe2rGCy8VQ1ln4w4Dblvv3SuuyZkZOK5UmYxZ3UbXs4XSrSkU5gZA==202511190347570001";
+            String key = "iIbnIjG6NzUtwzRA";
             
             System.out.println("=== HMAC-MD5签名测试 ===");
             System.out.println("原始数据: " + data);

+ 24 - 27
src/main/java/com/zsElectric/boot/common/util/OkHttpUtil.java

@@ -72,33 +72,30 @@ public class OkHttpUtil {
     /**
      * 执行 POST 请求(表单数据),返回 JsonObject
      */
-//    public JsonObject doPostForm(String url, Map<String, Object> formData, Map<String, String> headers) throws IOException {
-//        FormBody.Builder formBodyBuilder = new FormBody.Builder();
-//        if (formData != null) {
-//            formData.forEach((k, v) -> formBodyBuilder.add(k, String.valueOf(v)));
-//        }
-//
-//        Request.Builder requestBuilder = new Request.Builder()
-//                .url(url)
-//                .addHeader("Content-Type", "application/json; charset=utf-8")
-//                .post(formBodyBuilder.build());
-//
-//        if (MapUtil.isNotEmpty(headers)) {
-//            headers.forEach(requestBuilder::addHeader);
-//        }
-//
-//        Request request = requestBuilder.build();
-//        request.header("Content-Type:application/json; charset=utf-8");
-//        log.info("POST 请求 URL: " + url);
-//        log.info("POST 请求表单数据: " + formData);
-//        try (Response response = okHttpClient.newCall(request).execute()) {
-//            if (!response.isSuccessful()) {
-//                throw new IOException("Unexpected code: " + response);
-//            }
-//            String responseBody = response.body().string();
-//            return JsonParser.parseString(responseBody).getAsJsonObject();
-//        }
-//    }
+    public JsonObject doPostForm(String url, String json, Map<String, String> headers) throws IOException {
+        RequestBody body = RequestBody.create(json, JSON);
+
+        Request.Builder requestBuilder = new Request.Builder()
+                .url(url)
+                .post(body);
+
+        // 设置 headers
+        if (MapUtil.isNotEmpty(headers)) {
+            headers.forEach(requestBuilder::addHeader);
+        }
+        requestBuilder.addHeader("Content-Type", "application/json;charset=utf-8");
+
+        Request request = requestBuilder.build();
+        log.info("POST 请求 URL: " + url);
+        log.info("POST 请求 JSON 数据: " + json);
+        try (Response response = okHttpClient.newCall(request).execute()) {
+            if (!response.isSuccessful()) {
+                throw new IOException("Unexpected code: " + response);
+            }
+            String responseBody = response.body().string();
+            return JsonParser.parseString(responseBody).getAsJsonObject();
+        }
+    }
 
     public JsonObject doPostForm(String url, Map<String, Object> formData, Map<String, String> headers) throws IOException {
         FormBody.Builder formBodyBuilder = new FormBody.Builder();

+ 0 - 169
src/main/java/com/zsElectric/boot/common/util/electric/AesCryptoUtil.java

@@ -1,169 +0,0 @@
-package com.zsElectric.boot.common.util.electric;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.codec.binary.Base64;
-import org.springframework.stereotype.Component;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.nio.charset.StandardCharsets;
-import java.security.SecureRandom;
-
-/**
- * AES加解密工具类(Key和IV作为参数传入)
- * 算法:AES/CBC/PKCS5Padding
- * 密钥长度:128位(16字节)
- * 偏移量:16字节
- */
-@Slf4j
-@Component
-public class AesCryptoUtil {
-
-    // AES加密算法/模式/填充方式[1,4](@ref)
-    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
-    private static final String AES = "AES";
-
-    /**
-     * AES加密
-     * @param content 待加密内容
-     * @param key 密钥(16字节)
-     * @param iv 偏移量(16字节)
-     * @return Base64编码的加密结果
-     */
-    public static String encrypt(String content, String key, String iv) {
-        return encrypt(content, key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8));
-    }
-
-    /**
-     * AES加密(字节数组参数)
-     * @param content 待加密内容
-     * @param key 密钥字节数组(16字节)
-     * @param iv 偏移量字节数组(16字节)
-     * @return Base64编码的加密结果
-     */
-    public static String encrypt(String content, byte[] key, byte[] iv) {
-        try {
-            // 参数校验[2](@ref)
-            validateKeyAndIv(key, iv);
-            
-            // 创建密钥和偏移量规范[4](@ref)
-            SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
-            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
-            
-            // 初始化加密器[1](@ref)
-            Cipher cipher = Cipher.getInstance(ALGORITHM);
-            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
-            
-            // 执行加密[5](@ref)
-            byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
-            
-            // Base64编码返回
-            return Base64.encodeBase64String(encrypted);
-            
-        } catch (Exception e) {
-            log.error("AES加密失败: {}", e.getMessage(), e);
-            throw new RuntimeException("加密失败: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * AES解密
-     * @param encryptedContent Base64编码的加密内容
-     * @param key 密钥(16字节)
-     * @param iv 偏移量(16字节)
-     * @return 解密后的原始内容
-     */
-    public static String decrypt(String encryptedContent, String key, String iv) {
-        return decrypt(encryptedContent, key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8));
-    }
-
-    /**
-     * AES解密(字节数组参数)
-     * @param encryptedContent Base64编码的加密内容
-     * @param key 密钥字节数组(16字节)
-     * @param iv 偏移量字节数组(16字节)
-     * @return 解密后的原始内容
-     */
-    public static String decrypt(String encryptedContent, byte[] key, byte[] iv) {
-        try {
-            // 参数校验
-            validateKeyAndIv(key, iv);
-            
-            // 创建密钥和偏移量规范[4](@ref)
-            SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
-            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
-            
-            // 初始化解密器[1](@ref)
-            Cipher cipher = Cipher.getInstance(ALGORITHM);
-            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
-            
-            // Base64解码并解密[5](@ref)
-            byte[] encryptedBytes = Base64.decodeBase64(encryptedContent);
-            byte[] decrypted = cipher.doFinal(encryptedBytes);
-            
-            return new String(decrypted, StandardCharsets.UTF_8);
-            
-        } catch (Exception e) {
-            log.error("AES解密失败: {}", e.getMessage(), e);
-            throw new RuntimeException("解密失败: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 验证密钥和偏移量是否符合要求[2](@ref)
-     */
-    private static void validateKeyAndIv(byte[] key, byte[] iv) {
-        if (key == null || (key.length != 16 && key.length != 24 && key.length != 32)) {
-            throw new IllegalArgumentException("密钥长度必须为16、24或32字节");
-        }
-        if (iv == null || iv.length != 16) {
-            throw new IllegalArgumentException("偏移量必须为16字节");
-        }
-    }
-
-    /**
-     * 生成随机密钥(16/24/32字节)[5](@ref)
-     */
-    public static byte[] generateRandomKey(int keySize) {
-        if (keySize != 16 && keySize != 24 && keySize != 32) {
-            throw new IllegalArgumentException("密钥长度必须是16、24或32字节");
-        }
-        byte[] key = new byte[keySize];
-        new SecureRandom().nextBytes(key);
-        return key;
-    }
-
-    /**
-     * 生成随机偏移量(16字节)[5](@ref)
-     */
-    public static byte[] generateRandomIv() {
-        byte[] iv = new byte[16];
-        new SecureRandom().nextBytes(iv);
-        return iv;
-    }
-
-    /**
-     * 将字节数组转换为十六进制字符串(用于调试和存储)
-     */
-    public static String bytesToHex(byte[] bytes) {
-        StringBuilder sb = new StringBuilder();
-        for (byte b : bytes) {
-            sb.append(String.format("%02x", b));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * 将十六进制字符串转换为字节数组
-     */
-    public static byte[] hexToBytes(String hexString) {
-        int len = hexString.length();
-        byte[] data = new byte[len / 2];
-        for (int i = 0; i < len; i += 2) {
-            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
-                    + Character.digit(hexString.charAt(i + 1), 16));
-        }
-        return data;
-    }
-}

+ 3 - 0
src/main/java/com/zsElectric/boot/common/util/electric/ApiToken.java

@@ -1,5 +1,6 @@
 package com.zsElectric.boot.common.util.electric;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 import java.time.Duration;
@@ -24,11 +25,13 @@ public class ApiToken {
     /**
      * 令牌获取时间
      */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime obtainTime;
     
     /**
      * 令牌过期时间
      */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime expireTime;
     
     /**

+ 6 - 4
src/main/java/com/zsElectric/boot/common/util/electric/ChargingUtil.java

@@ -43,13 +43,15 @@ public class ChargingUtil {
 
             requestParms
                     .setOperatorID(ConnectivityConstants.OPERATOR_ID)
-                    .setData(AesCryptoUtil.encrypt(queryParms.toString(),ConnectivityConstants.PLATFORM_DATA_SECRET,
+                    .setData(AESCryptoUtils.encrypt(new Gson().toJson(queryParms),ConnectivityConstants.PLATFORM_DATA_SECRET,
                             ConnectivityConstants.PLATFORM_DATA_SECRET_IV))
                     .setTimeStamp(result.getTimestamp())
                     .setSeq(result.getSequence())
-                    .setSig(HmacMD5Util.genSign(requestParms.getOperatorID(),requestParms.getData(),requestParms.getTimeStamp(),requestParms.getSeq(),ConnectivityConstants.SIG_SECRET));
+                    .setSig(HmacMD5Util.genSign(requestParms.getOperatorID(),requestParms.getData(),
+                            requestParms.getTimeStamp(),
+                            requestParms.getSeq(),ConnectivityConstants.PLATFORM_SIG_SECRET));
 
-            JsonObject response = okHttpUtil.doPostForm(url, BeanUtil.beanToMap(requestParms), headers);
+            JsonObject response = okHttpUtil.doPostForm(url, new Gson().toJson(requestParms), headers);
 
             if (Objects.isNull(response)) {
                 log.error("调用第三方接口获取Token失败");
@@ -103,7 +105,7 @@ public class ChargingUtil {
                         break;
                 }
             }
-            String decodeData = AesCryptoUtil.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
+            String decodeData = AESCryptoUtils.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
                     ConnectivityConstants.PLATFORM_DATA_SECRET_IV);
             return gson.fromJson(decodeData, JsonObject.class);
         }catch (Exception e){

+ 11 - 11
src/main/java/com/zsElectric/boot/common/util/electric/ElectricTokenManager.java

@@ -1,6 +1,7 @@
 package com.zsElectric.boot.common.util.electric;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
 import com.zsElectric.boot.common.constant.ConnectivityConstants;
@@ -154,29 +155,27 @@ public class ElectricTokenManager {
             QueryTokenParms queryTokenParms = new QueryTokenParms();
             queryTokenParms
                     .setOperatorID(ConnectivityConstants.OPERATOR_ID)
-                    .setOperatorSecret(ConnectivityConstants.OPERATOR_SECRET);
+                    .setOperatorSecret(ConnectivityConstants.PLATFORM_OPERATOR_SECRET);
 
             RequestParmsEntity requestParms = new RequestParmsEntity();
             SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate();
 
             requestParms
                     .setOperatorID(ConnectivityConstants.OPERATOR_ID)
-                    .setData(AesCryptoUtil.encrypt(queryTokenParms.toString(), ConnectivityConstants.DATA_SECRET,
-                            ConnectivityConstants.DATA_SECRET_IV))
+                    .setData(AESCryptoUtils.encrypt(new Gson().toJson(queryTokenParms), ConnectivityConstants.PLATFORM_DATA_SECRET,
+                            ConnectivityConstants.PLATFORM_DATA_SECRET_IV))
                     .setTimeStamp(result.getTimestamp())
                     .setSeq(result.getSequence())
-                    .setSig(HmacMD5Util.genSign(requestParms.getOperatorID(), requestParms.getData(), requestParms.getTimeStamp(), requestParms.getSeq(), ConnectivityConstants.SIG_SECRET));
+                    .setSig(HmacMD5Util.genSign(requestParms.getOperatorID(), requestParms.getData(), requestParms.getTimeStamp(),
+                            requestParms.getSeq(), ConnectivityConstants.PLATFORM_SIG_SECRET));
 
-            JsonObject response = okHttpUtil.doPostForm(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_TOKEN, BeanUtil.beanToMap(requestParms), null);
+            JsonObject response = okHttpUtil.doPostForm(ConnectivityConstants.TEST_DOMAIN + ConnectivityConstants.QUERY_TOKEN, new Gson().toJson(requestParms), null);
 
             if (Objects.isNull(response)) {
                 log.error("调用第三方接口获取Token失败");
                 return null;
             }
-
-
-            Gson gson = new Gson();
-            ResponseParmsEntity responseParms = gson.fromJson(response, ResponseParmsEntity.class);
+            ResponseParmsEntity responseParms = new Gson().fromJson(response, ResponseParmsEntity.class);
             String data = responseParms.getRet() + responseParms.getMsg() + responseParms.getData();
             boolean verify = HmacMD5Util.verify(data, ConnectivityConstants.PLATFORM_SIG_SECRET, responseParms.getSig());
             if (!verify) {
@@ -205,9 +204,10 @@ public class ElectricTokenManager {
                         break;
                 }
             }
-            String decodeData = AesCryptoUtil.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
+            String decodeData = AESCryptoUtils.decrypt(responseParms.getData(), ConnectivityConstants.PLATFORM_DATA_SECRET,
                     ConnectivityConstants.PLATFORM_DATA_SECRET_IV);
-            QueryTokenResponseData responseData = gson.fromJson(decodeData, QueryTokenResponseData.class);
+
+            QueryTokenResponseData responseData = new Gson().fromJson(decodeData, QueryTokenResponseData.class);
 
             if (responseData.getSuccStat() == 1) {
                 //0-无,1-OperatorID无效

+ 22 - 10
src/main/java/com/zsElectric/boot/config/RedisConfig.java

@@ -1,10 +1,15 @@
 package com.zsElectric.boot.config;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
 import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 /**
  * Redis 配置
@@ -20,23 +25,30 @@ public class RedisConfig {
      * <p>
      * 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
      *
-     * @param redisConnectionFactory {@link RedisConnectionFactory}
+     * @param connectionFactory {@link RedisConnectionFactory}
      * @return {@link RedisTemplate}
      */
     @Bean
-    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
 
-        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
-        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(connectionFactory);
 
-        redisTemplate.setKeySerializer(RedisSerializer.string());
-        redisTemplate.setValueSerializer(RedisSerializer.json());
+        // 配置支持 LocalDateTime 的 ObjectMapper
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
 
-        redisTemplate.setHashKeySerializer(RedisSerializer.string());
-        redisTemplate.setHashValueSerializer(RedisSerializer.json());
+        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(objectMapper);
 
-        redisTemplate.afterPropertiesSet();
-        return redisTemplate;
+        template.setDefaultSerializer(serializer);
+        template.setKeySerializer(new StringRedisSerializer());
+        template.setValueSerializer(serializer);
+        template.setHashKeySerializer(new StringRedisSerializer());
+        template.setHashValueSerializer(serializer);
+
+        template.afterPropertiesSet();
+        return template;
     }
 
 }