guoshilong
2023-08-09 34c2f71c21bc56b359b27ad92061477a0ff3ff48
Merge remote-tracking branch 'origin/jtdev' into jtdev
2 files modified
1 files added
389 ■■■■■ changed files
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/controller/UserPushController.java 42 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SecurityUtil.java 157 ●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java 190 ●●●●● patch | view | raw | blame | history
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/controller/UserPushController.java
@@ -4,8 +4,8 @@
import cn.gistack.system.user.service.IUserService;
import cn.gistack.system.user.sync.dto.AccountBean;
import cn.gistack.system.user.sync.dto.ApiPushDTO;
import cn.gistack.system.user.sync.util.GenerateUtil;
import cn.gistack.system.user.sync.util.SecurityUtil;
import cn.gistack.system.user.sync.util.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import lombok.AllArgsConstructor;
import org.springblade.core.mp.support.Condition;
@@ -16,7 +16,9 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @PROJECT_NAME: skjcmanager
@@ -39,7 +41,8 @@
    @PostMapping("/userpush")
    public R userPush(@RequestBody ApiPushDTO apiPushDTO) {
        String syncData = SecurityUtil.decryptAES(apiPushDTO.getSyncData(), appSecret);
        System.out.println("请求参数:" + JSON.toJSONString(apiPushDTO));
        String syncData = SecurityUtil.decryptAES(apiPushDTO.getSyncData(), MD5Util.getMD5String(appSecret));
        List<AccountBean> accountBeanList = JSONArray.parseArray(syncData, AccountBean.class);
        //循环list
@@ -83,4 +86,37 @@
        return R.data(apiPushDTO.getTimestamp());
    }
//    public static void main(String[] args) {
//        Map<String,String> attributes = new HashMap<>();
//        // "staffCode": "15563378977",
//        //        "realName": "zhliu",
//        //        "email": null,
//        //        "phone": "15563378977",
//        //        "employeeNum": "zhliu",
//        //        "idCard": null,
//        //        "employeeStatus": "1",
//        //        "userType": "0",
//        //        "sort": "1"
//        attributes.put("staffCode","15563378977");
//        attributes.put("realName","zhliu");
//        attributes.put("email",null);
//        attributes.put("phone","15563378977");
//        attributes.put("employeeNum","zhliu");
//        attributes.put("idCard", null);
//        attributes.put("employeeStatus","1");
//        attributes.put("userType","0");
//        attributes.put("sort","1");
////        String syncData = SecurityUtil.encryptAES(JSON.toJSONString(attributes), MD5Util.getMD5String(appSecret));
////        System.out.println("加密后:" + syncData);
////        String jiemi  = SecurityUtil.decryptAES(syncData, MD5Util.getMD5String(appSecret));
////        System.out.printf("解密后:" + jiemi);
//
//        String tmp = "XmcEvdSFwXYvpAcoDZW5NKYmx1IyvXEJLdRnwLFTBUAhN0DheUZnVMb5y3IuMoASUsBrb82PrbKefa754brKNgvgmKvJxehhkLAWALy/33jX7lA+YjXLzWqLrr/fp6AEVEcLuz8rf+ZvkHcJsyAzPclaxPvd6R0LV0KIiyalnVbhfGCjPPvhYNnOBZh3vNPssn1Z+REywWtHD15G9/zEhpuEno6gPtNxtk1PoqxebCuxZvKRGT2DM/KL0GO4dpY8ksm6bgDcHXcB5oKIdwxb1N7yRQ5DJsIZkz9e/Zo720ObReXCLI9KdErQyOM/uxTqnHE7FVeGP3f5sebTH2WeKyI2mefwGrZYrnGAfxbZOr8pDpXiDs/SF35SHdvC5rRAgfEMm9r1pA74xtb9vplcmvQQqB7aDMbRLQWOo3R9U4KqwU2yvq1L6pZLeWbFVKH5ei40APzcdGS3K8QpcfyzkU5BuR4VgHvNMS9xEkIAbZ9flLBEPE08CReEQ9rJQr8AP5SZ4d394aIxJnJHRxwCNUuMMEdj0/C8UDfwwQMxUQPDlc4tA+DHDjgAwc641gu8IVTqmWjb5+kT3v0Xe9pEPgg4C9kDn0JOoZxsG0UpHdWU5QObGhI+i/j9Ip1T2pcvJdy0eAU0HSAtfBu56i/Li9L97jqtWSog05H3ni3mLjbFrDBARfEd4mOSW9F89fya7ORMM/SnW0qD+Db9dS8Uu/eKdpct/xm4i74HbknnUEdr85txKh0QSNahkirT88yot4YJTPNKOMRQQKrLgj5nAopZIqaoOHvHaTNR6ii0Se8TLrUeUwEYdoW/dpB/3ql5nt34igYLSdll7jw3jRHTHZ6oqvd7CGyQvqSdimcq/BCitx2TkorxMDvoawjYm++eCjW6CDHC2gl4P4e8yf2R5yIjEMxBlU7daMZAcGjY2IO0/WXmW0lbpBXwydHiSSW3oTEL/pwGEFszW2mocMmyB5V2jce8xt4Kog2s1hjNuISZ7J5QIWph0SXONELyCpMud1wFxWpj6o1nZGSV/yzZhTfgk+yEItgRYlDnAEew8lTKSTaOX97/G2Xc1fJZFYYzOuVhtzakrJ3LUjxYEOKr5+NkEx0utKt3UV7o5oH7RB2lvpjREZ5vnvZDIDAWpBnH0thYaq2s2a7KJj92gbgWAjbyFVK2RIOREs3Lw0SxWUh2hfwCPNj7oXzS+X7vwmf4R3aw2vfI20sLKbhyezGKFgmtHQkn1lvdbMDj7hNfNXG6UE698Fa6Fq2Vr7DhPdlAZVmrHiPPaRSKi6QlOaeNtGe8J3PrJpYbtlDV/e8+DRA0kAcrofhktiOFjQcOMY0Gu3qJt6tV4L1ODndZCHdmWIytMsyAB+tEoeEFhIMpWZAzosCws+Kx78L/Uy7t/LwDP+vlN5RoFQnjeAdZTMtriNDmG+8KqrOWOz4EM5uMICro7bbuMBxpAZkV348dlUoQZkzd8jog0f2NkAygL+qIWeQtcOrjYU5S00q9LVd2swK1szpOIaRFbq8YIWLKaEWhPDZIJmtYYhlKIHJ3D0bJ0HoiENjSLyYYEEVmQF4j9wmIBvcyIr46NDF47Jo9K8hVNJObc41qecxh2kiqNh3lST50rgdma5uOJH1dsnS4gYCYWM6jY/8JMLEoqhTO//ljcI9XpnvzSN3F1jkhCJtV7yhBN20wczEdSB63VFxwQU5C5MEbfiLs0kXhgogXQ4e9";
//        String jiemi2  = SecurityUtil.decryptAES(tmp, MD5Util.getMD5String(appSecret));
//        System.out.printf("解密后:" + jiemi2);
//
//        SignUtil.getSign(appId, 1L, appSecret, "", "post", attributes);
//
//    }
}
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;
    }
}
skjcmanager/skjcmanager-service/skjcmanager-user/src/main/java/cn/gistack/system/user/sync/util/SignBuilder.java
New file
@@ -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;
    }
}