From 47310c3a2c55e2d50b78c44dc213ebb8b67faac5 Mon Sep 17 00:00:00 2001
From: xieb <vip_xiaobin810@163.com>
Date: Wed, 09 Aug 2023 17:08:37 +0800
Subject: [PATCH] IAM用户推送接口

---
 skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java |  157 +++++--------------------
 skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java  |  190 +++++++++++++++++++++++++++++++
 2 files changed, 223 insertions(+), 124 deletions(-)

diff --git a/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java b/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java
index 1d574de..eefbe86 100644
--- a/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java
+++ b/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java
@@ -1,18 +1,22 @@
 package cn.gistack.system.user.sync.util;
 
-
 import cn.gistack.system.user.sync.constant.CimsConstants;
 import cn.gistack.system.user.sync.exception.BusinessException;
 import cn.gistack.system.user.sync.exception.ErrorCode;
+import org.springframework.util.StringUtils;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.SecretKeySpec;
 
 /**
  * AES加密解密
+ *
  * @author wd
  */
 public class SecurityUtil {
+
+    private SecurityUtil() {
+    }
 
     /**
      * AES加密
@@ -21,12 +25,9 @@
      * @return 密文
      */
     public static String encryptAES(String content, String secretKey) {
-        checkParam(content,secretKey);
+        checkParam(content, secretKey);
         //AES加密
-        String encryptResultStr = encrypt(content, secretKey);
-        //BASE64位加密
-        encryptResultStr = ebotongEncrypto(encryptResultStr);
-        return encryptResultStr;
+        return encrypt(content, secretKey);
     }
 
     /**
@@ -35,17 +36,16 @@
      * @param encryptResultStr 密文
      * @return 明文
      */
-    public static String decryptAES(String encryptResultStr,String secretKey) {
-        checkParam(encryptResultStr,secretKey);
+    public static String decryptAES(String encryptResultStr, String secretKey) {
+        checkParam(encryptResultStr, secretKey);
         try {
             // BASE64位解密
-            String decrpt = ebotongDecrypto(encryptResultStr);
-            byte[] decryptFrom = hexToByteArray(decrpt);
+            byte[] decryptFrom = Base64.getDecoder().decode(encryptResultStr);
+
             //AES解密
             byte[] decryptResult = decrypt(decryptFrom, secretKey);
             return new String(decryptResult);
         } catch (Exception e) {
-            e.printStackTrace();
             // 当密文不规范时会报错,可忽略,但调用的地方需要考虑
             throw new BusinessException(ErrorCode.CONTENT_EMPTY_ERROR);
         }
@@ -54,42 +54,31 @@
     /**
      * 校验参数
      */
-    private static void checkParam(String encryptResultStr,String secretKey){
-        if (isBlank(encryptResultStr)) {
+    private static void checkParam(String encryptResultStr, String secretKey) {
+        if (StringUtils.isEmpty(encryptResultStr)) {
             throw new BusinessException(ErrorCode.CONTENT_EMPTY_ERROR);
         }
-		if (isBlank(secretKey)) {
-			throw new BusinessException(ErrorCode.SECRET_KEY_ERROR);
-		}
-//        if (secretKey.length() != CimsConstants.SECRET_KEY_LENGTH || !secretKey.matches(CimsConstants.SECRET_KEY_REGEX)) {
-//            throw new BusinessException(ErrorCode.SECRET_KEY_ERROR);
-//        }
+        if (!secretKey.matches(CimsConstants.SECRET_KEY_REGEX)) {
+            throw new BusinessException(ErrorCode.SECRET_KEY_ERROR);
+        }
     }
 
 
     /**
      * 加密
      *
-     * @param content  需要加密的内容
-     * @param password 加密密码
+     * @param content 需要加密的内容
+     * @param secretKey  加密密钥
      * @return
      */
-    private static String encrypt(String content, String password) {
+    private static String encrypt(String content, String secretKey) {
         try {
-            byte[] raw = password.getBytes(CimsConstants.SECRET_KEY_ENCODING);
-            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
-            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
-            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
-            byte[] byteRresult = cipher.doFinal(content.getBytes(CimsConstants.SECRET_KEY_ENCODING));
-            StringBuffer sb = new StringBuffer();
-            for (int i = 0; i < byteRresult.length; i++) {
-                String hex = Integer.toHexString(byteRresult[i] & 0xFF);
-                if (hex.length() == 1) {
-                    hex = '0' + hex;
-                }
-                sb.append(hex.toUpperCase());
-            }
-            return sb.toString();
+            byte[] raw = secretKey.getBytes(CimsConstants.SECRET_KEY_ENCODING);
+            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//NOSONAR  sonar扫描忽略这段代码
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
+            byte[] bytes = cipher.doFinal(content.getBytes(CimsConstants.SECRET_KEY_ENCODING));
+            return Base64.getEncoder().encodeToString(bytes);
         } catch (Exception e) {
             throw new BusinessException(ErrorCode.CONTENT_ENCODE_ERROR);
         }
@@ -98,99 +87,19 @@
     /**
      * 解密
      *
-     * @param content  待解密内容
-     * @param password 解密密钥
+     * @param content 待解密内容
+     * @param secretKey  解密密钥
      * @return
      */
-    private static byte[] decrypt(byte[] content, String password) {
+    private static byte[] decrypt(byte[] content, String secretKey) {
         try {
-            byte[] raw = password.getBytes(CimsConstants.SECRET_KEY_ENCODING);
-            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
-            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
-            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
-            byte[] result = cipher.doFinal(content);
-            return result;
+            byte[] raw = secretKey.getBytes(CimsConstants.SECRET_KEY_ENCODING);
+            SecretKeySpec keySpec = new SecretKeySpec(raw, "AES");
+            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//NOSONAR  sonar扫描忽略这段代码
+            cipher.init(Cipher.DECRYPT_MODE, keySpec);
+            return cipher.doFinal(content);
         } catch (Exception e) {
-            e.printStackTrace();
             throw new BusinessException(ErrorCode.SECRET_DECODE_ERROR);
         }
     }
-
-    /**
-     * hex字符串转byte数组
-     * @param inHex 待转换的Hex字符串
-     * @return  转换后的byte数组结果
-     */
-    private static byte[] hexToByteArray(String inHex){
-        int hexlen = inHex.length();
-        byte[] result;
-        if (hexlen % 2 == 1){
-            //奇数
-            hexlen++;
-            result = new byte[(hexlen/2)];
-            inHex="0"+inHex;
-        }else {
-            //偶数
-            result = new byte[(hexlen/2)];
-        }
-        int j=0;
-        for (int i = 0; i < hexlen; i+=2){
-            result[j] = hexToByte(inHex.substring(i,i+2));
-            j++;
-        }
-        return result;
-    }
-
-    /**
-     * Hex字符串转byte
-     * @param inHex 待转换的Hex字符串
-     * @return  转换后的byte
-     */
-    private static byte hexToByte(String inHex){
-        return (byte)Integer.parseInt(inHex,16);
-    }
-
-    /**
-     * Base64加密字符串
-     */
-    private static String ebotongEncrypto(String str) {
-        String result = str;
-        if (str != null && str.length() > 0) {
-            try {
-                byte[] encodeByte = str.getBytes(CimsConstants.SECRET_KEY_ENCODING);
-                result = Base64.getEncoder().encodeToString(encodeByte);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-        // base64加密超过一定长度会自动换行 需要去除换行符
-        return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
-    }
-
-    /**
-     * Base64解密字符串
-     */
-    private static String ebotongDecrypto(String str) {
-        byte[] encodeByte = Base64.getDecoder().decode(str);
-        return new String(encodeByte);
-    }
-
-    /**
-     * 判断字符串是否为空
-     * @param cs
-     * @return
-     */
-    private static boolean isBlank(final CharSequence cs) {
-        int strLen;
-        if (cs == null || (strLen = cs.length()) == 0) {
-            return true;
-        }
-        for (int i = 0; i < strLen; i++) {
-            if (!Character.isWhitespace(cs.charAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
 }
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java b/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java
new file mode 100644
index 0000000..a16d4c0
--- /dev/null
+++ b/skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java
@@ -0,0 +1,190 @@
+package cn.gistack.system.user.sync.util;
+
+import java.util.*;
+
+/**
+ * 计算签名工具类;
+ * 签名计算方法为:
+ *
+ * sign = MD5(自然排序(排除签名的所有参数) + [requestPath] + [requestMethod[POST|GET]] + secretKey);
+ * 注意 requestMethod 已经经过大写转换处理;
+ */
+public class SignBuilder {
+    public final static String TIMESTAMP_KEY = "timestamp";
+    public final static String CLIENT_ID_KEY = "clientId";
+    public final static String SIGN_KEY = "sign";
+    public final static String SALT_KEY = "salt";
+
+    private final String clientId;
+    private final String clientSecret;
+
+    private Long timestamp;
+
+    private String salt;
+
+    private String requestPath;
+    private String requestMethod;
+
+    private String sign;
+
+    private final Map<String, Object> allParam = new TreeMap<String, Object>();
+
+
+    private SignBuilder(String clientId, String clientSecret) {
+        this.clientId = clientId;
+        this.clientSecret = clientSecret;
+    }
+
+
+    public static SignBuilder create(String clientId, String clientSecret) {
+        return new SignBuilder(clientId, clientSecret);
+    }
+
+    /**
+     * 构建实例的时候 自动生成时间戳,通过 SignBuilder.getTimestamp() 获取时间戳
+     * @param clientId OAuth2.0中客户端ID 也称为 appId
+     * @param clientSecret OAuth2.0中客户端秘钥 也称为 appSecret
+     */
+    public static SignBuilder createWithTimestampAndSalt(String clientId, String clientSecret) {
+        SignBuilder result = new SignBuilder(clientId, clientSecret);
+        return result.timestamp(System.currentTimeMillis()).salt();
+    }
+
+
+    public SignBuilder param(String key, Object value) {
+        if (sign != null) {
+            throw new RuntimeException("sign has been generated, can't change the param");
+        }
+
+        if (SIGN_KEY.equals(key)) {
+            return this;
+        }
+
+        if (TIMESTAMP_KEY.equals(key)) {
+            this.timestamp = Long.valueOf(String.valueOf(value));
+        }
+
+        if (SALT_KEY.equals(key)) {
+            this.salt = String.valueOf(value);
+        }
+
+        allParam.put(key, value);
+        return this;
+    }
+
+    public SignBuilder params(Map<String, Object> params) {
+        if (params == null) {
+            return this;
+        }
+
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            param(entry.getKey(), entry.getValue());
+        }
+        return this;
+    }
+
+    /**
+     * 设置时间戳
+     * @param timestamp 时间戳
+     */
+    public SignBuilder timestamp(Long timestamp) {
+        param(TIMESTAMP_KEY, timestamp);
+        return this;
+    }
+
+    public SignBuilder requestPath(String requestPath) {
+        if (sign != null) {
+            throw new RuntimeException("sign has been generated, can't change the param");
+        }
+        this.requestPath = requestPath;
+        return this;
+    }
+
+    public SignBuilder requestMethod(String requestMethod) {
+        if (sign != null) {
+            throw new RuntimeException("sign has been generated, can't change the param");
+        }
+        if (requestMethod != null) {
+            this.requestMethod = requestMethod.toUpperCase();
+        }
+        return this;
+    }
+
+    public SignBuilder salt() {
+        return salt(Math.random());
+    }
+
+    public SignBuilder salt(Object salt) {
+        param(SALT_KEY, salt);
+        return this;
+    }
+
+    /**
+     * 获取 http 请求参数
+     */
+    public Map<String, Object> getQueryParamMap() {
+        Map<String, Object> result = new HashMap<String, Object>(allParam);
+        result.put(SIGN_KEY, sign());
+        return result;
+    }
+
+    /**
+     * debug使用,获取签名前的原文;<br>
+     * 秘钥已脱敏,使用 <code>{clientSecret}</code> 代替
+     */
+    public String getRawDataBeforeSign() {
+        return rawDataBeforeSign() + "{clientSecret}";
+    }
+
+
+    private String rawDataBeforeSign() {
+        allParam.put(CLIENT_ID_KEY, clientId);
+
+        if (timestamp != null && !allParam.containsKey(TIMESTAMP_KEY)) {
+            allParam.put(TIMESTAMP_KEY, timestamp);
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (Object value : allParam.values()) {
+            if (value != null) {
+                sb.append(String.valueOf(value).trim());
+            }
+        }
+        if (requestPath != null) {
+            sb.append(requestPath);
+        }
+
+        if (requestMethod != null) {
+            sb.append(requestMethod.toUpperCase());
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 计算当前参数的签名,已经计算签名后,所有参数不可更改
+     */
+    public String sign() {
+        if (sign != null) {
+            return sign;
+        }
+
+        this.sign = MD5Util.getMD5String(rawDataBeforeSign() + clientSecret);
+        if (this.sign == null) {
+            return "";
+        }
+        return this.sign.toUpperCase();
+    }
+
+    public String getSalt() {
+        return salt;
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public Long getTimestamp() {
+        return timestamp;
+    }
+}

--
Gitblit v1.9.3