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; } }