Browse Source

feat(electric): 实现第三方认证token查询接口

- 修改AES加密工具类中的密钥和初始化向量常量引用
- 更新开发环境配置文件中第三方接口白名单路径及JWT密钥长度
- 优化充电相关工具类中请求参数实体的设置方式
- 在ElectricTokenManager中调整请求参数构建逻辑
- 更新HmacMD5工具类中的测试数据签名值
- 在JwtTokenUtil中增加已有有效token的复用逻辑并实现查询方法
- 移除RequestParmsEntity上的@Data注解并手动添加JsonProperty注解
- 删除冗余的RequestParmsEntitys类
- 完善ThirdPartyAuthController中query_token接口的签名验证与token生成逻辑
- 增加日志记录和异常处理机制提升接口稳定性
wzq 3 weeks ago
parent
commit
c0abaed1e7

+ 75 - 19
src/main/java/com/zsElectric/boot/auth/controller/ThirdPartyAuthController.java

@@ -4,48 +4,104 @@ import com.google.gson.Gson;
 import com.zsElectric.boot.common.constant.ConnectivityConstants;
 import com.zsElectric.boot.common.util.AESCryptoUtils;
 import com.zsElectric.boot.common.util.HmacMD5Util;
+import com.zsElectric.boot.common.util.electric.QueryTokenResponseData;
 import com.zsElectric.boot.common.util.electric.RequestParmsEntity;
-import com.zsElectric.boot.common.util.electric.RequestParmsEntitys;
 import com.zsElectric.boot.common.util.electric.ResponseParmsEntity;
 import com.zsElectric.boot.common.util.electric.queryToken.*;
 
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
+@Slf4j
 @RestController
 @RequestMapping("/api/third-party")
 public class ThirdPartyAuthController {
 
+    @Resource
+    private JwtTokenUtil jwtTokenUtil;
 
     @PostMapping("/v1/get")
-    @TokenRequired
+//    @TokenRequired
     public String get() {
         return "get";
     }
 
     @PostMapping("/query_token")
-    public ResponseParmsEntity getToken(@RequestBody RequestParmsEntitys request) throws Exception {
-        //todo 验证签名
-        if (!HmacMD5Util.verify(request.getOperatorID() + request.getData() + request.getTimeStamp() + request.getSeq(),
-                ConnectivityConstants.SIG_SECRET, request.getSig())) {
-            return new ResponseParmsEntity()
+    public ResponseParmsEntity getToken(@RequestBody RequestParmsEntity request) throws Exception {
+        ResponseParmsEntity responseParmsEntity = new ResponseParmsEntity();
+        try {
+            //验证签名
+            if (!HmacMD5Util.verify(request.getOperatorID() + request.getData() + request.getTimeStamp() + request.getSeq(),
+                    ConnectivityConstants.SIG_SECRET, request.getSig())) {
+                return responseParmsEntity
+                        .setRet(4001)
+                        .setMsg("签名验证失败")
+                        .setData("")
+                        .setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                                ConnectivityConstants.SIG_SECRET));
+            }
+
+            String data = request.getData();
+            String string = AESCryptoUtils.decrypt(data, ConnectivityConstants.DATA_SECRET, ConnectivityConstants.DATA_SECRET_IV);
+            QueryTokenRequestParms queryTokenRequestParms = new Gson().fromJson(string, QueryTokenRequestParms.class);
+            if (queryTokenRequestParms == null || queryTokenRequestParms.getOperatorID() == null || queryTokenRequestParms.getOperatorSecret() == null) {
+                responseParmsEntity
+                        .setRet(4003)
+                        .setMsg("参数错误")
+                        .setData("");
+                responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                        ConnectivityConstants.SIG_SECRET));
+                return responseParmsEntity;
+            }
+
+            //判断运营商ID与密钥是否正确
+            if (!queryTokenRequestParms.getOperatorID().equals(ConnectivityConstants.PLATFORM_OPERATOR_ID) && !queryTokenRequestParms.getOperatorSecret().equals(ConnectivityConstants.PLATFORM_OPERATOR_SECRET)) {
+                responseParmsEntity
+                        .setRet(4004)
+                        .setMsg("OperatorID或OperatorSecret错误!")
+                        .setData("");
+                responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                        ConnectivityConstants.SIG_SECRET));
+            }
+
+            //todo redis获取token,不存在则创建
+            String accessToken = jwtTokenUtil.generateToken(queryTokenRequestParms.getOperatorID());
+            Integer remainingTTL = jwtTokenUtil.getRemainingTTL(accessToken).intValue();
+            //构建Data(token信息)
+            QueryTokenResponseData queryTokenResponseData = new QueryTokenResponseData();
+            queryTokenResponseData.setOperatorID(queryTokenRequestParms.getOperatorID());
+            queryTokenResponseData.setAccessToken(accessToken);
+            queryTokenResponseData.setTokenAvailableTime(remainingTTL);
+            queryTokenResponseData.setSuccStat(0);
+            queryTokenResponseData.setFailReason(0);
+
+            log.info("生成token信息:{}", new Gson().toJson(queryTokenResponseData));
+
+            String encodeData = AESCryptoUtils.encrypt(new Gson().toJson(queryTokenResponseData), ConnectivityConstants.PLATFORM_DATA_SECRET,
+                    ConnectivityConstants.PLATFORM_DATA_SECRET_IV);
+
+            responseParmsEntity
                     .setRet(0)
-                    .setMsg("签名验证失败")
-                    .setData("")
-                    .setSig("");
+                    .setMsg("成功")
+                    .setData(encodeData)
+                    .setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                            ConnectivityConstants.SIG_SECRET));
+            return responseParmsEntity;
+        } catch (Exception e) {
+            log.error("系统错误:{}", e.getMessage());
+            responseParmsEntity
+                    .setRet(500)
+                    .setMsg("系统错误")
+                    .setData("");
+            responseParmsEntity.setSig(HmacMD5Util.genSign(responseParmsEntity.getRet(), responseParmsEntity.getMsg(), responseParmsEntity.getData(),
+                    ConnectivityConstants.SIG_SECRET));
+            return responseParmsEntity;
         }
-        
-        String data = request.getData();
-        String string = AESCryptoUtils.decrypt(data, ConnectivityConstants.DATA_SECRET, ConnectivityConstants.DATA_SECRET_IV);
-        QueryTokenRequestParms queryTokenRequestParms = new Gson().fromJson(string, QueryTokenRequestParms.class);
-        return null;
     }
 }
 
 
-
-
-
-    
 //    @GetMapping("/token/validate")
 //    public ResponseEntity<?> validateToken(@RequestHeader("Authorization") String authHeader) {
 //        if (authHeader == null || !authHeader.startsWith("Bearer ")) {

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

@@ -150,8 +150,8 @@ public class AESCryptoUtils {
         try {
             // 测试数据
             String originalData = "{\"OperatorID\":\"MA6DP6BE7\", \"OperatorSecret\":\"Sov2Gs590CLUbx4g\"}";
-            String key = PLATFORM_DATA_SECRET;   // 16字节密钥
-            String iv = PLATFORM_DATA_SECRET_IV;   // 16字节初始化向量
+            String key = DATA_SECRET;   // 16字节密钥
+            String iv = DATA_SECRET_IV;   // 16字节初始化向量
 
             System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
             System.out.println("原始数据: " + originalData);

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

@@ -211,7 +211,7 @@ public class HmacMD5Util {
     public static void main(String[] args) {
         try {
             // 测试数据
-            String data = "MA6DP6BE7Gpw3qXJ39TZp2zcW4wtvp67IVLon1I+IutsQmq67mPt/+W5BJ14Kcc65siGU+sFjLZcHAdWm8nX/d3xLKI3ldA==202511210705520001";
+            String data = "MA6DP6BE7WiZdxjtssyPEyh8q5v9ZVz0fCBOQQysXhCO8S0pq/+R7UTnfBQUQKS84LWix46SCMKMAsB26Bg5EI8qyg4tvbw==202511210705520001";
             String key = "U9xFXjjdYAycq30C";
             
             System.out.println("=== HMAC-MD5签名测试 ===");

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

@@ -41,13 +41,12 @@ public class ChargingUtil {
             RequestParmsEntity requestParms = new RequestParmsEntity();
             SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate();
 
-            requestParms
-                    .setOperatorID(ConnectivityConstants.OPERATOR_ID)
-                    .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.setOperatorID(ConnectivityConstants.OPERATOR_ID);
+            requestParms.setData(AESCryptoUtils.encrypt(new Gson().toJson(queryParms),ConnectivityConstants.PLATFORM_DATA_SECRET,
+                            ConnectivityConstants.PLATFORM_DATA_SECRET_IV));
+            requestParms.setTimeStamp(result.getTimestamp());
+            requestParms .setSeq(result.getSequence());
+            requestParms.setSig(HmacMD5Util.genSign(requestParms.getOperatorID(),requestParms.getData(),
                             requestParms.getTimeStamp(),
                             requestParms.getSeq(),ConnectivityConstants.PLATFORM_SIG_SECRET));
 

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

@@ -160,12 +160,12 @@ public class ElectricTokenManager {
             SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate();
 
             requestParms
-                    .setOperatorID(ConnectivityConstants.OPERATOR_ID)
-                    .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(),
+                    .setOperatorID(ConnectivityConstants.OPERATOR_ID);
+            requestParms.setData(AESCryptoUtils.encrypt(new Gson().toJson(queryTokenParms), ConnectivityConstants.PLATFORM_DATA_SECRET,
+                            ConnectivityConstants.PLATFORM_DATA_SECRET_IV));
+            requestParms.setTimeStamp(result.getTimestamp());
+            requestParms.setSeq(result.getSequence());
+            requestParms.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);

+ 46 - 2
src/main/java/com/zsElectric/boot/common/util/electric/RequestParmsEntity.java

@@ -1,13 +1,12 @@
 package com.zsElectric.boot.common.util.electric;
 
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
 import java.io.Serial;
 import java.io.Serializable;
 
-@Data
 @Accessors(chain = true)
 public class RequestParmsEntity implements Serializable {
 
@@ -39,4 +38,49 @@ public class RequestParmsEntity implements Serializable {
      */
     private String Sig;
 
+    @JsonProperty("OperatorID")
+    public String getOperatorID() {
+        return OperatorID;
+    }
+
+    public void setOperatorID(String operatorID) {
+        OperatorID = operatorID;
+    }
+
+    @JsonProperty("Data")
+    public String getData() {
+        return Data;
+    }
+
+    public void setData(String data) {
+        Data = data;
+    }
+
+    @JsonProperty("TimeStamp")
+    public String getTimeStamp() {
+        return TimeStamp;
+    }
+
+    public void setTimeStamp(String timeStamp) {
+        TimeStamp = timeStamp;
+    }
+
+    @JsonProperty("Seq")
+    public String getSeq() {
+        return Seq;
+    }
+
+    public void setSeq(String seq) {
+        Seq = seq;
+    }
+
+    @JsonProperty("Sig")
+    public String getSig() {
+        return Sig;
+    }
+
+    public void setSig(String sig) {
+        Sig = sig;
+    }
+
 }

+ 0 - 70
src/main/java/com/zsElectric/boot/common/util/electric/RequestParmsEntitys.java

@@ -1,70 +0,0 @@
-package com.zsElectric.boot.common.util.electric;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serial;
-import java.io.Serializable;
-
-@Accessors(chain = true)
-public class RequestParmsEntitys implements Serializable {
-
-    @Serial
-    private static final long serialVersionUID = 1L;
-
-    private String OperatorID;
-
-    private String Data;
-
-    private String TimeStamp;
-
-    private String Seq;
-
-    private String Sig;
-
-    @JsonProperty("OperatorID")
-    public String getOperatorID() {
-        return OperatorID;
-    }
-
-    public void setOperatorID(String operatorID) {
-        OperatorID = operatorID;
-    }
-
-    @JsonProperty("Data")
-    public String getData() {
-        return Data;
-    }
-
-    public void setData(String data) {
-        Data = data;
-    }
-
-    @JsonProperty("TimeStamp")
-    public String getTimeStamp() {
-        return TimeStamp;
-    }
-
-    public void setTimeStamp(String timeStamp) {
-        TimeStamp = timeStamp;
-    }
-
-    @JsonProperty("Seq")
-    public String getSeq() {
-        return Seq;
-    }
-
-    public void setSeq(String seq) {
-        Seq = seq;
-    }
-
-    @JsonProperty("Sig")
-    public String getSig() {
-        return Sig;
-    }
-
-    public void setSig(String sig) {
-        Sig = sig;
-    }
-}

+ 39 - 0
src/main/java/com/zsElectric/boot/common/util/electric/queryToken/JwtTokenUtil.java

@@ -14,7 +14,10 @@ import org.springframework.stereotype.Component;
 
 import javax.crypto.SecretKey;
 import java.util.Date;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * JWT Token工具类
@@ -44,6 +47,13 @@ public class JwtTokenUtil {
      * 生成JWT Token
      */
     public String generateToken(String operatorId) {
+
+        // 先查询Redis中是否已有有效Token
+        String existingToken = getExistingValidToken(operatorId);
+        if (existingToken != null) {
+            return existingToken;
+        }
+
         Date now = new Date();
         Date expiryDate = new Date(now.getTime() + expireSeconds * 1000);
         
@@ -154,4 +164,33 @@ public class JwtTokenUtil {
             redisTemplate.opsForSet().remove(operatorTokensKey, token);
         }
     }
+
+    /**
+     * 查询操作员现有的有效Token
+     * 如果Token存在且剩余过期时间大于1分钟,则返回该Token
+     */
+    private String getExistingValidToken(String operatorId) {
+        String operatorTokensKey = REDIS_OPERATOR_TOKENS_PREFIX + operatorId;
+        Set<Object> tokenObjects = redisTemplate.opsForSet().members(operatorTokensKey);
+        Set<String> tokens = tokenObjects.stream()
+                .filter(Objects::nonNull)
+                .map(Object::toString)
+                .collect(Collectors.toSet());
+
+        if (tokens != null && !tokens.isEmpty()) {
+            for (String token : tokens) {
+                String tokenKey = REDIS_TOKEN_PREFIX + token;
+                // 检查token是否存在
+                if (Boolean.TRUE.equals(redisTemplate.hasKey(tokenKey))) {
+                    // 获取剩余过期时间
+                    Long remainingTTL = redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
+                    // 如果剩余时间大于1分钟(60秒),则返回该token
+                    if (remainingTTL != null && remainingTTL > 60) {
+                        return token;
+                    }
+                }
+            }
+        }
+        return null;
+    }
 }

+ 2 - 2
src/main/resources/application-dev.yml

@@ -89,7 +89,7 @@ security:
     - /ws/** # WebSocket接口
     - /dev/v1/linkData/** #互联互通
     - /api/third-party/query_token
-    - /api/third-party/get
+    - /api/third-party/v1/get
   # 非安全端点路径,完全绕过 Spring Security 的安全控制
   unsecured-urls:
     - ${springdoc.swagger-ui.path}
@@ -302,5 +302,5 @@ ai:
 # 第三方认证配置
 third-party:
   jwt:
-    secret: "vgct1TZ4ZikKjaaeIiq3LUwIvpmcgYa6"
+    secret: "vgct1TZ4ZikKjaaeIiq3LUwIvpmcgYa6ABCD1234EFGH5678IJKL9012MNOP3456"
     expire: 7200