From 4229247f776e190383f379a32dc274675abb33ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E8=89=BA=E9=B9=8F?= <18382071280@163.com> Date: Wed, 23 Aug 2023 16:37:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(*)=20=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4Li?= =?UTF-8?q?censeAnalysis=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LicenseAnalysis/pom.xml | 88 +++++ .../cloud/licenseAnalysis/api/Result.java | 105 ++++++ .../cloud/licenseAnalysis/api/ResultCode.java | 118 ++++++ .../launcher/CompanyValid.java | 109 ++++++ .../launcher/LauncherValid.java | 345 ++++++++++++++++++ .../launcher/LicenseValid.java | 303 +++++++++++++++ .../licenseAnalysis/launcher/SystemInit.java | 154 ++++++++ .../licenseAnalysis/pojo/Application.java | 77 ++++ .../cloud/licenseAnalysis/pojo/License.java | 141 +++++++ .../cloud/licenseAnalysis/pojo/Param.java | 53 +++ .../cloud/licenseAnalysis/utils/AESUtil.java | 100 +++++ .../cloud/licenseAnalysis/utils/MD5.java | 66 ++++ .../licenseAnalysis/utils/MachineInfo.java | 48 +++ .../utils/Sha1WithRsaUtil.java | 255 +++++++++++++ .../licenseAnalysis/utils/StaticResource.java | 36 ++ .../licenseAnalysis/utils/SystemLogger.java | 337 +++++++++++++++++ 16 files changed, 2335 insertions(+) create mode 100644 LicenseAnalysis/pom.xml create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/Result.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/ResultCode.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/CompanyValid.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LauncherValid.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LicenseValid.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/SystemInit.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Application.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/License.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Param.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/AESUtil.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MD5.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MachineInfo.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/Sha1WithRsaUtil.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/StaticResource.java create mode 100644 LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/SystemLogger.java diff --git a/LicenseAnalysis/pom.xml b/LicenseAnalysis/pom.xml new file mode 100644 index 0000000..f5d1b38 --- /dev/null +++ b/LicenseAnalysis/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + cn.crtech.license_analysis + LicenseAnalysis + 1.0.1 + + + + cn.crtech.cloud.dependencies + Dependencies + 1.0.1 + + + + + + 8 + 8 + 1.8 + UTF-8 + + 5.8.3 + 1.2.83 + 1.9.13 + 2.1.3 + 1.0.1 + + + + + + org.projectlombok + lombok + true + + + + commons-codec + commons-codec + + + + org.springframework + spring-core + + + + org.apache.commons + commons-lang3 + + + + com.google.code.gson + gson + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + + + + org.dom4j + dom4j + ${dom4j.version} + + + diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/Result.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/Result.java new file mode 100644 index 0000000..3b5db56 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/Result.java @@ -0,0 +1,105 @@ +package cn.crtech.cloud.licenseAnalysis.api; + +/** + * 统一返回实体 + * + * @Author: TYP + * @Date: 2022-03-01 16:46 + */ + +public class Result { + private int code; + private String message; + private Object data; + private boolean success; + + public Result() { + } + + public Result(String message, boolean success) { + this.message = message; + this.success = success; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public static Result success() { + Result result = new Result(); + result.setCode(ResultCode.SUCCESS.code()); + result.setMessage(ResultCode.SUCCESS.message()); + result.success = true; + return result; + } + + public static Result success(Object data) { + Result result = Result.success(); + result.setCode(ResultCode.SUCCESS.code()); + result.setMessage(ResultCode.SUCCESS.message()); + result.data = data; + return result; + } + + public static Result success(Object data, String operation) { + Result result = Result.success(); + result.data = data; + result.message = operation; + return result; + } + + public static Result error() { + Result result = new Result(); + result.success = false; + return result; + } + + public static Result error(Object object) { + Result result = new Result(); + result.success = false; + result.data = object; + return result; + } + + public static Result error(ResultCode data) { + Result result = Result.error(); + result.setCode(data.code()); + result.setMessage(data.message()); + return result; + } + + public static Result error(Object data, String operation) { + Result result = Result.error(); + result.data = data; + result.message = operation; + return result; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/ResultCode.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/ResultCode.java new file mode 100644 index 0000000..d7cbd27 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/api/ResultCode.java @@ -0,0 +1,118 @@ +package cn.crtech.cloud.licenseAnalysis.api; + +/** + * 统一返回体信息枚举 + * + * @Author: TYP + * @Date: 2022-03-01 16:47 + */ +public enum ResultCode { + /******************************** 系统通用 ************************************************/ + SUCCESS(200, "success"), + + SYSTEM_ERROR(10000, "系统异常"), + + /******************************** 自定义异常 ************************************************/ + EXIST_SAME_BUSINESS_LICENSE_COMPANY(10001, " 已存在相同营业执照号的公司"), + COMPANY_EXIST_MANAGER(10002, "当前企业已存在管理员用户"), + COMPANY_NOT_EXIST(10003, "参数异常,不存在当前公司"), + COMPANY_EXIST_SAME_ROE(10004, "公司已存在同名或同编号参数"), + COMPANY_EXIST_SAME_MOBILE_USER(10005, "本公司已存在该手机号绑定的员工"), + + SYSTEM_EXIST_SAME_MOBILE_USER(10006, "系统已存在该手机号邦迪的用户"), + + CURRENT_USER_NOT_EXIST(10007, "当前用户不存在"), + + CURRENT_APPLICATION_GROUP_EXIST(10008, "请勿重复添加同名(同标识)产品分组"), + CURRENT_APPLICATION_GROUP_HAD_APPS(10009, "当前产品分组内存在产品数据,不可删除"), + CURRENT_APPLICATION_GROUP_EXIST_SAME_NAME_OR_CODE_APP(10010, "当前产品分组已存在同名或同标识产品,请勿重复添加"), + + CURRENT_POPEDOM_OR_MENU_ALREADY_EXIST(10011, "已经存在同编号权限/菜单"), + CURRENT_POPEDOM_OR_MENU_HAD_CHILD_ITEM(10012, "不可删除!当前权限/菜单存在子数据"), + + CURRENT_COMPANY_EXIST_SAME_CODE_APP(10013, "当前产品已存在授权版本,不允许重复添加或添加多个版本"), + + CURRENT_COMPANY_ALREADY_EXIST_MANAGER_ROLE(10014, "当前公司已存在管理员角色,请勿重复添加!"), + CURRENT_COMPANY_NOT_EXIST_MANAGER_ROLE(10015, "当前公司暂不存在管理员角色,无法添加管理员账号!"), + + LICENSE_DECODE_ERROR_PUBLIC_KEY(10016, "解码公钥为空"), + LICENSE_DECODE_ERROR_SIGN(10017, "签名为空"), + LICENSE_DECODE_ERROR_MACHINE_KEY(10018, "机器码为空"), + LICENSE_DECODE_ERROR_PRODUCT_SERIES_KEY(10019, "产品数据为空"), + LICENSE_DECODE_ERROR(10020, "license授权码解析异常"), + LICENSE_DECODE_ERROR_SIGN_ERROR(10021, "文件签名校验不一致"), + + CURRENT_CLIENT_NOT_PUBLISH(10022, "当前客户端未建立授权信息,请联系商务"), + CURRENT_CLIENT_LICENSE_OVER_DUE(10023, "当前客户端授权信息已过期,请联系商务"), + CURRENT_CLIENT_LICENSE_NOT_AUTH(10024, "当前客户端未授权,请联系商务"), + CURRENT_CLIENT_LICENSE_DELETE(10025, "当前客户端授权信息已删除,请联系商务"), + + REST_CONNECT_ERROR(10026, "客户端访问院端接口失败"), + + SYSTEM_EXIST_SAME_NAME_OR_CODE_ROLE(10027, "系统已存在同名(同标识)角色,请勿重复添加"), + CURRENT_ROLE_BIND_USER(10028, "当前角色已经绑定用户,无法直接删除"), + + CURRENT_MOBILE_ALREADY_EXIST(10029, "当前手机已绑定用户,不可重复添加"), + + CURRENT_ACCOUNT_NOT_EXIST(10030, "用户未注册"), + CURRENT_INPUT_PASSWORD_ERROR(10031, "账号或密码输入错误"), + CURRENT_ACCOUNT_ALREADY_FROZEN(10032, "当前账号已被停用,请联系管理员进行处理"), + + CURRENT_OLD_PASS_INPUT_ERROR(10033, "当前账号旧密码输入错误"), + + CURRENT_SYSTEM_ALREADY_EXIST_SAME_NAME_OR_CODE_DICTIONARY_TYPE(10034, "系统已存在同名或者同标识的字典类型,无法重复添加/修改"), + CURRENT_SYSTEM_ALREADY_EXIST_SAME_NAME_OR_CODE_DICTIONARY_DATA(10035, "当前字典类型中已存在同名或者同标识的字典数据,无法重复添加/修改"), + + EXIST_SAME_CODE_COMPANY(10036, " 已存在相同编码标识的公司"), + LICENSE_DOWNLOAD_FAIL(10037, "系统异常,授权文件下载失败"), + + CR_FUNCTION_EXISTED(10038, "系统存在同名或同标识功能,请勿重复添加"), + + MODIFY_PASSWORD_CHECK_ERROR(10038, "两次密码输入不一致"), + MODIFY_PASSWORD_SAME_PASS_ERROR(10039, "新密码与旧密码不可以相同"), + + REGISTER_USER_EXIST_ERROR(10037, "用户已存在,无法注册"), + + MODIFY_USERINFO_ERROR(10100, "没有用户信息被修改"), + + /************************************* 授权 登录鉴权相关 ********************************************************/ + SYSTEM_HAD_NOT_LICENSE(20000, "客户端未授权"), + SYSTEM_QUERY_PARAM_ERROR(20001, "请求参数异常"), + TOKEN_OVER_DUE(20002, "TOKEN已过期"), + USER_ALREADY_REMOTE_LOGIN(20003, "用户已在其他地方登录"), + USER_HAD_NOT_THIS_AUTH(20004, "当前用户没有此授权"), + USER_ALREADY_LOGOUT(20005, "用户已登出"), + + SYSTEM_APPLICATION_AUTH_OVER(20006,"授权产品已过期"), + SYSTEM_APPLICATION_HAD_NOT_AUTH(20007,"当前系统未授权PIVAS云·药品知识库·院端产品"), + ; + + private int code; + private String message; + + ResultCode(int code, String message) { + this.code = code; + this.message = message; + } + + public void setCode(int code) { + this.code = code; + } + + public void setMessage(String message) { + this.message = message; + } + + public int code() { + return this.code; + } + + public String message() { + return this.message; + } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/CompanyValid.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/CompanyValid.java new file mode 100644 index 0000000..12402ac --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/CompanyValid.java @@ -0,0 +1,109 @@ +package cn.crtech.cloud.licenseAnalysis.launcher; + +import cn.crtech.cloud.licenseAnalysis.pojo.License; +import cn.crtech.cloud.licenseAnalysis.utils.AESUtil; +import cn.crtech.cloud.licenseAnalysis.utils.MD5; +import cn.crtech.cloud.licenseAnalysis.utils.StaticResource; +import cn.crtech.cloud.licenseAnalysis.utils.SystemLogger; + +import java.io.*; + +/** + * desc + * + * @author: TYP + * @date: 2022-12-28 15:27 + */ + +public class CompanyValid { + // 是否已经设置CompanyCode + private static boolean hadCompanyCode = false; + + private static SystemLogger logger = new SystemLogger(CompanyValid.class); + + public static void valid() { + if (License.getLicense().getCompanyCode() == null) { + boolean read = readCompanyInfo(); + if (!read) { + logger.info("系统暂未配置授权公司秘钥,允许客户自行进行配置!"); + } else { + setHad(true); + } + } else { + setHad(true); + } + } + + private static String generateKey() throws IOException { + String key = "Crtech2021"; + return MD5.md5(key.getBytes("UTF-8")); + } + + public static void createCompanyInfo(String companyCode) { + FileWriter fw = null; + try { + File file = new File(StaticResource.FILE_PATH + "/company"); + if (file.exists()) { + file.delete(); + } + + String key = generateKey(); + String cipher = AESUtil.encrypt(companyCode, key); + fw = new FileWriter(StaticResource.FILE_PATH + "/company"); + fw.write(cipher); + License.setCompanyCode(companyCode); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (fw != null) { + try { + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private static boolean readCompanyInfo() { + File file = new File(StaticResource.FILE_PATH + "/company"); + if (file.exists()) { + InputStreamReader is = null; + BufferedReader br = null; + try { + is = new InputStreamReader(new FileInputStream(file), "UTF-8"); + br = new BufferedReader(is); + String lineTxt = null; + StringBuffer str = new StringBuffer(); + while ((lineTxt = br.readLine()) != null) { + str.append(lineTxt); + } + String key = generateKey(); + String decrypt = AESUtil.decrypt(str.toString(), key); + License.setCompanyCode(decrypt); + return true; + } catch (Exception e) { + e.printStackTrace(); + logger.error("解析Company配置信息异常:" + e.getMessage()); + } + } + return false; + } + + public static void setHad(boolean set) { + hadCompanyCode = set; + } + + public static boolean getHad() { + return hadCompanyCode; + } + + public static void delCompanyInfo() { + File file = new File(StaticResource.FILE_PATH + "/company"); + if (file.exists()) { + file.delete(); + } + setHad(false); + License.setCompanyCode(null); + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LauncherValid.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LauncherValid.java new file mode 100644 index 0000000..154bd13 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LauncherValid.java @@ -0,0 +1,345 @@ +package cn.crtech.cloud.licenseAnalysis.launcher; + +import cn.crtech.cloud.licenseAnalysis.utils.AESUtil; +import cn.crtech.cloud.licenseAnalysis.utils.MD5; +import cn.crtech.cloud.licenseAnalysis.utils.MachineInfo; +import cn.crtech.cloud.licenseAnalysis.utils.StaticResource; +import cn.crtech.cloud.licenseAnalysis.utils.SystemLogger; +import com.alibaba.fastjson.JSON; +import org.apache.commons.lang3.ObjectUtils; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * 授权码校验 + * + * @author inaisen + */ +public class LauncherValid { + private static final SystemLogger logger = new SystemLogger(LauncherValid.class); + + /** + * 主板序列号 + */ + private static String BaseBoradSN; + + /** + * CPU序列号 + */ + private static String CpuSN; + + /** + * 硬盘S/N + */ + private static String DiskSN; + + /** + * 授权唯一UUID + */ + public static String UD; + + /** + * 授权文件物理地址 + */ + private static File cipherFile = new File(StaticResource.FILE_PATH, StaticResource.CHIPER_FILE); + + public static File licenseFile = new File(StaticResource.FILE_PATH, "license"); + + private static String cipherContent; + + /** + * 初始化服务器(电脑)运行环境信息 + */ + public static void init() { + BaseBoradSN = MachineInfo.getSerialnumber(); + CpuSN = MachineInfo.getprocessorid(); + DiskSN = MachineInfo.getDisksNumber(); + } + + /** + * 验证授权码文件,有误则删除重新生成 + * + * @throws Exception 异常信息 + */ + public static void valid() throws Exception { + boolean f = false; + logger.info("开始验证授权码文件 =============> "); + try { + f = validCipher(); + logger.info("授权码文件校验结果 ==> {} ", f); + } catch (Exception e) { + logger.error("验证授权码文件异常 ==> {} ", e.getMessage()); + } + + if (!f) { + logger.info("验证授权码文件失败 重新生成"); + generateCipher(); + } + } + + /** + * 获取机器码数据 + * + * @return 返回数据 + */ + public static String getCode() { + return cipherContent; + } + + /** + * 校验机械码数据是否正确 + * + * @param licenseContent 机械码 + * @return 返回判断结果 + * @throws IOException 信息读取异常 + */ + public static boolean validLicenseCipher(String licenseContent) throws IOException { + String key = generateKey(); + return validCipher(AESUtil.decrypt(licenseContent, key)); + } + + /** + * 解密授权码,验证系统信息 + * + * @return 处理结果 + * @throws IOException 文件读取异常 + */ + private static boolean validCipher() throws Exception { + String key = generateKey(); + String cipher = loadCipher(); + String content = AESUtil.decrypt(cipher, key); + if (validCipher(content)) { + cipherContent = cipher; + return true; + } else { + if (checkMachineFile(content, null, null, true)) { + cipherContent = cipher; + return true; + } + return false; + } + } + + /** + * 校验机械码内容 + * + * @param content 内容 + * @return 返回判断结果 + * @throws IOException 数据读取异常 + */ + private static boolean validCipher(String content) throws IOException { + if (!ObjectUtils.isEmpty(content)) { + init(); + + Map cipherMap = JSON.parseObject(content, Map.class); + if (!BaseBoradSN.equals(cipherMap.get("bs"))) { + logger.error("签名校验异常(BS) ==> 继续校验是否由于编码BOM导致错误 ====> "); + String bs = deleteUTF8Bom(cipherMap.get("bs").toString()); + + if (!BaseBoradSN.equals(bs)) { + logger.error("签名校验异常(BS) ==> 本机(" + BaseBoradSN + ") != 解析数据(" + cipherMap.get("bs") + ")"); + return false; + } + logger.info("====> 签名校验异常(BS) 是由于文件编码BOM导致异常 现已进行处理 结果允许通过"); + } + + if (!CpuSN.equals(cipherMap.get("cs"))) { + logger.error("签名校验异常(CS) ==> 继续校验是否由于编码BOM导致错误 ====> "); + String cs = deleteUTF8Bom(cipherMap.get("cs").toString()); + + if (!CpuSN.equals(cs)) { + logger.error("签名校验异常(CS) ==> 本机(" + CpuSN + ") != 解析数据(" + cipherMap.get("cs") + ")"); + return false; + } + logger.info("====> 签名校验异常(CS) 是由于文件编码BOM导致异常 现已进行处理 结果允许通过"); + } + + if (!DiskSN.equals(cipherMap.get("ds"))) { + logger.error("签名校验异常(DS) ==> 继续校验是否由于编码BOM导致错误 ====> "); + String ds = deleteUTF8Bom(cipherMap.get("ds").toString()); + + if (!DiskSN.equals(ds)) { + logger.error("签名校验异常(DS) ==> 本机(" + DiskSN + ") != 解析数据(" + cipherMap.get("ds") + ")"); + return false; + } + logger.info("====> 签名校验异常(DS) 是由于文件编码BOM导致异常 现已进行处理 结果允许通过"); + } + + if (!StaticResource.RUN_PATH.equals(cipherMap.get("rp"))) { + logger.error("签名校验异常(RUN_PATH) ==> 继续校验是否由于编码BOM导致错误 ====> "); + String rp = deleteUTF8Bom(cipherMap.get("rp").toString()); + + if (!StaticResource.RUN_PATH.equals(rp)) { + logger.error("签名校验异常(RUN_PATH) ==> 本机(" + StaticResource.RUN_PATH + ") != 解析数据(" + cipherMap.get("rp") + ")"); + return false; + } + logger.info("====> 签名校验异常(RUN_PATH) 是由于文件编码BOM导致异常 现已进行处理 结果允许通过"); + } + + long mt = cipherFile.lastModified(); + long ct = Long.parseLong(cipherMap.get("ct").toString()); + if (Math.abs(ct - mt) <= 1000) { + UD = cipherMap.get("ud").toString(); + return true; + } + logger.error("签名校验异常(文件时间异常) ==> Math.abs(ct(" + ct + ") - mt(" + mt + ")) > 1000"); + } + return false; + } + + private static String deleteUTF8Bom(String fileStr) { + byte[] UTF8_BOM_BYTES = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}; + byte[] bytes = fileStr.getBytes(); + if (bytes[0] == UTF8_BOM_BYTES[0] + && bytes[1] == UTF8_BOM_BYTES[1] + && bytes[2] == UTF8_BOM_BYTES[2]) { + return new String(bytes, 3, bytes.length - 3); + } + return fileStr; + } + + /** + * 读取授权码文件 + * + * @return 返回处理结果 + * @throws IOException 异常 + */ + public static String loadCipher() throws IOException { + FileInputStream fis = null; + try { + if (!cipherFile.exists()) { + cipherFile.createNewFile(); + } + fis = new FileInputStream(cipherFile); + int iAvail = fis.available(); + byte[] bytes = new byte[iAvail]; + fis.read(bytes); + return new String(bytes, "UTF-8"); + } catch (IOException e) { + throw e; + } finally { + if (fis != null) { + fis.close(); + } + } + } + + /** + * 加密授权码写入授权文件 + * + * @throws IOException 异常 + */ + private static void generateCipher() throws IOException { + String key = generateKey(); + String content = generateCipherContent(); + String cipher = AESUtil.encrypt(content, key); + if (cipherFile.exists()) { + cipherFile.delete(); + } else { + if (licenseFile.exists()) { + licenseFile.delete(); + } + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(cipherFile); + fos.write(cipher.getBytes("UTF-8")); + fos.flush(); + cipherContent = cipher; + } catch (FileNotFoundException e) { + e.printStackTrace(); + File confFile = new File(StaticResource.FILE_PATH); + if (!confFile.exists()) { + confFile.mkdir(); + generateCipher(); + } + } finally { + if (fos != null) { + fos.close(); + } + } + } + + /** + * 获得本机授权文件密钥 + * + * @return 返回生成结果 + * @throws IOException 异常 + */ + public static String generateKey() throws IOException { + String key = "Crtech2021"; + return MD5.md5(key.toString().getBytes("UTF-8")); + } + + /** + * 获得本机物理地址信息,返回待加密的JSON + * + * @return 返回处理结果 + */ + private static String generateCipherContent() { + Map map = new HashMap(); + map.put("bs", MachineInfo.getSerialnumber()); + map.put("cs", MachineInfo.getprocessorid()); + map.put("ds", MachineInfo.getDisksNumber()); + map.put("rp", StaticResource.RUN_PATH); + UD = UUID.randomUUID().toString(); + map.put("ud", UD); + map.put("ct", System.currentTimeMillis()); + return JSON.toJSONString(map); + } + + /** + * 机器码数据信息解析 + * + * @param machineInfoKey 机械码 + * @return 返回解析内容 + * @throws Exception 异常 + */ + public static Map decodeMachineInfoKey(String machineInfoKey) throws Exception { + String key = generateKey(); + String cipher = AESUtil.decrypt(machineInfoKey, key); + Map cipherMap = JSON.parseObject(cipher, Map.class); + return cipherMap; + } + + /** + * 授权码文件数据校验 + * + * @param systemInfo 系统信息 + * @param machineInfoKey 机械码 + * @param prod 是否正式校验 false时表示测试 + * @return 返回判断结果 + * @throws Exception 异常 + */ + public static boolean checkMachineFile(String contentInfo, String systemInfo, String machineInfoKey, boolean prod) throws Exception { + if (machineInfoKey == null || "".equals(machineInfoKey)) { + machineInfoKey = LauncherValid.loadCipher(); + } + Map machineInfoMap = decodeMachineInfoKey(machineInfoKey); + + if (contentInfo == null || "".equals(contentInfo)) { + String key = generateKey(); + contentInfo = AESUtil.decrypt(systemInfo, key); + } + + if (!ObjectUtils.isEmpty(contentInfo) || !ObjectUtils.isEmpty(machineInfoMap)) { + Map cipherMap = JSON.parseObject(contentInfo, Map.class); + + if (prod) { + return (machineInfoMap.get("bs").toString().equals(cipherMap.get("bs")) + && machineInfoMap.get("cs").toString().equals(cipherMap.get("cs")) + && machineInfoMap.get("ds").toString().equals(cipherMap.get("ds")) + && StaticResource.RUN_PATH.equals(cipherMap.get("rp"))); + } else { + return (machineInfoMap.get("bs").toString().equals(cipherMap.get("bs")) + && machineInfoMap.get("cs").toString().equals(cipherMap.get("cs")) + && machineInfoMap.get("ds").toString().equals(cipherMap.get("ds")) + && machineInfoMap.get("rp").toString().equals(cipherMap.get("rp"))); + } + } + return false; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LicenseValid.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LicenseValid.java new file mode 100644 index 0000000..41297a6 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/LicenseValid.java @@ -0,0 +1,303 @@ +package cn.crtech.cloud.licenseAnalysis.launcher; + +import cn.crtech.cloud.licenseAnalysis.utils.AESUtil; +import cn.crtech.cloud.licenseAnalysis.utils.Sha1WithRsaUtil; +import cn.crtech.cloud.licenseAnalysis.pojo.Application; +import cn.crtech.cloud.licenseAnalysis.pojo.License; +import cn.crtech.cloud.licenseAnalysis.utils.StaticResource; +import cn.crtech.cloud.licenseAnalysis.utils.SystemLogger; + +import java.io.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 授权信息校验/初始化/处理工具类 + */ +public class LicenseValid { + public static File licenseFile = new File(StaticResource.FILE_PATH, "license"); + + private static SystemLogger logger = new SystemLogger(LicenseValid.class); + + public static void valid() { + try { + // 判断文件是否存在 + if (!licenseFile.isFile() || !licenseFile.exists()) { + logger.error("文件不存在"); + throw new Exception("文件不存在"); + } + readLicense(new FileInputStream(licenseFile)); + } catch (Exception e) { + writeError("载入授权文件[" + licenseFile.getAbsolutePath() + "]失败: " + e.getMessage()); + } + } + + /** + * 重写 license文件 方便读取自己传入的文件 + * + * @param file 文件 + */ + protected static void validByPersonalFile(File file) { + try { + // 判断文件是否存在 + if (!file.isFile() || !file.exists()) { + throw new Exception("文件不存在"); + } + readLicense(new FileInputStream(file)); + } catch (Exception e) { + writeError("载入授权文件[" + file.getAbsolutePath() + "]失败: " + e.getMessage()); + } + } + + /** + * 重写 根据传入的文件地址 读取license数据 + * + * @param filePath 文件地址 + */ + protected static void validByFilePath(String filePath) { + File file = new File(filePath); + validByPersonalFile(file); + } + + /** + * 读取license文件,返回publicKey,授权Json串,企业信息 + */ + public static void readLicense(InputStream is) throws Exception { + String encoding = "UTF-8"; + // 考虑到编码格式 + InputStreamReader read = null; + try { + read = new InputStreamReader(is, encoding); + BufferedReader bufferedReader = new BufferedReader(read); + String lineTxt = null; + List str = new ArrayList<>(); + int i = 0; + while ((lineTxt = bufferedReader.readLine()) != null) { + str.add(lineTxt); + i++; + } + //license中文本信息小于4行,则表示license文件有有误 + if (i != 4) { + logger.error("文本信息小于4行,license文件有有误"); + throw new Exception("文本信息小于4行,license文件有有误"); + } + String privateEncrypt = str.get(0); + String publicKey = str.get(1); + String application = str.get(2); + String sign = str.get(3); + String systemInfo = Sha1WithRsaUtil.decrypt(privateEncrypt, Sha1WithRsaUtil.getPublicKeyX509(publicKey)); + if (!LauncherValid.validLicenseCipher(systemInfo)) { + logger.error("validLicenseCipher ===> 系统签名认证错误"); + if (!LauncherValid.checkMachineFile(null, systemInfo, null, true)) { + logger.error("checkMachineFile ===> 系统签名认证错误"); + throw new Exception("checkMachineFile ===> 系统签名认证错误"); + } else { + logger.info("checkMachineFile ===> 系统签名认证通过"); + } + } + String json = AESUtil.decrypt(application, systemInfo); + // 验证签名 + if (!Sha1WithRsaUtil.verify(json, sign, publicKey)) { + logger.error("公有签名认证错误"); + throw new Exception("公有签名认证错误"); + } + write(json); + } finally { + if (read != null) { + read.close(); + } + } + } + + /** + * 读取license文件,返回publicKey,授权Json串,企业信息 + */ + public static void initLicenseFile(List info) { + try { + String privateEncrypt = info.get(0); + String publicKey = info.get(1); + String application = info.get(2); + String sign = info.get(3); + String systemInfo = Sha1WithRsaUtil.decrypt(privateEncrypt, Sha1WithRsaUtil.getPublicKeyX509(publicKey)); + if (!LauncherValid.validLicenseCipher(systemInfo)) { + logger.error("validLicenseCipher ===> 系统签名认证错误"); + if (!LauncherValid.checkMachineFile(null, systemInfo, null, true)) { + logger.error("checkMachineFile ===> 系统签名认证错误"); + throw new Exception("checkMachineFile ===> 系统签名认证错误"); + } else { + logger.info("checkMachineFile ===> 系统签名认证通过"); + } + } + + String json = AESUtil.decrypt(application, systemInfo); + // 验证签名 + if (!Sha1WithRsaUtil.verify(json, sign, publicKey)) { + logger.error("公有签名认证错误"); + throw new Exception("公有签名认证错误"); + } + + createLicense(info); + write(json); + } catch (Exception e) { + + } + } + + /** + * 授权文件信息写入 + * @param info 信息 + */ + private static void createLicense(List info) { + FileWriter fw = null; + try { + File file = new File(StaticResource.FILE_PATH + "/license"); + if (file.exists()) { + file.delete(); + } + + fw = new FileWriter(StaticResource.FILE_PATH + "/license"); + StringBuffer key = new StringBuffer(); + for (int i = 0; i <= (info.size() - 1); i++) { + key.append(info.get(i)); + if (i != (info.size() - 1)) { + key.append("\n"); + } + } + fw.write(key.toString()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (fw != null) { + try { + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 删除授权文件 + * @return 返回处理结果 + */ + public static boolean deleteLicense() { + File file = new File(StaticResource.FILE_PATH, "license"); + // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 + if (file.exists() && file.isFile()) { + if (file.delete()) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * 输出调用处理错误 + * @param errorMessage 错误信息 + */ + public static void writeError(String errorMessage) { + License.validLicense = false; + License.error = errorMessage; + logger.error(errorMessage); + deleteLicense(); + } + + /** + * 授权信息写入 + * @param licenseJSON 授权信息 + */ + public static void write(String licenseJSON) { + License.init(licenseJSON); + logger.info("license文件内容初始化完成"); + } + + /** + * 校验系统授权是否含有对应产品 + * + * @param appCode 产品标识 + * @return 返回判断结果 + */ + public static boolean checkApplication(String appCode) { + if (License.validLicense) { + List appList = License.getLicense().getApplications(); + for (Application app : appList) { + if (app.getCode().equals(appCode)) { + if (app.getExpiryTime() > new Date().getTime()) { + return false; + } else { + logger.error("产品[" + app.getName() + "]授权已过期,系统无法访问!"); + return true; + } + } + } + } + return false; + } + + /** + * 获取客户端授权产品信息 + * @param appCode 产品标识 + * @return 返回查询结果 + */ + public static Application getClientApplication(String appCode) { + if (License.validLicense) { + List appList = License.getLicense().getApplications(); + for (Application app : appList) { + if (app.getCode().equals(appCode)) { + return app; + } + } + } + logger.error("当前产品不存在或者未授权"); + return null; + } + + /** + * 自定义测试处理方法 + */ + public static void main(String[] args) { + String machineInfoKey = "cNSGlMwZy8GxLDTo3QxuAXsNj5XIaZYCCl3hEeO0wOrWA7qktwfFETgVUiLxgG5vCmP/9CW0siRwgKkPcAf++/0JUmzjGnEftmMb+D226GCpPjvQbAUFX7Hin+ycEfN2IkL6F6FchZLtdZoJOAbAn7RRv+eZB5hd2r9/TQr1IiEGYpQc5MX4mIGX4xFkt3G0RZrIRp0PK/2YHxaMvb7QPepzfsrwbs6XXQ6BrL/7m4xreW8JAt8nhjjCuaDSCkOU"; + + String licenseInfoKey = "ctn0H_0CZl2Y2R3XoCF_b41PGRjdyiejgd9XtdduI6FkHwTeMOFcxvFL-B5yd7fhj6HY7sEVuSP1yejcheavfDImEbvlzFrR44ZG1Ab-3KRQ6MT95i9pGV_hEgd22T61u4oNyoQg8hBaucT4mvZrML3JZLf4funvtj7cJi1Mp0nInL61p3oevq2Of8hhrLBpW9jUcWSZG07N4m5Fy4sBwCG6HmmF_WFABXPJ6R2LwhHqT8GG7aOqctVLK4lQddpf-iu0U8GHdqXxpDJpYJCo3Bxj148aJlYzrPOPbOcO6VC-LvlT_xt4rikXk-STHIDNeI9s9y6_UsXPn3xx1CJOQyqg3PPahf2uZENh_9FMNpqII7Mzs1vg5wnnVE3bnO4a9Ga-q06lu3QKdglK4f_RAzuJYFIm6HjLUhXFCwuTH7-RrUdFmiaQw48BlmZ3Htj9zb1RherKxKjQ4rJgF6b2HYI58tBeEnwz70RTO87MGU9RyHIL0UD89qwNIuH3-n3tnh-V5xT9gCld3VITRJ2Yi46O12WKQ6TW1zNEaVd3FrVypzS1P4FLOkmXsoePUUy4PZ2uZo6oSClQVndr76pmb1dg7Z9reaqXfG5OJsNGk7B9Fr6fm16HZYyzpipo_0_49aJu39Gqm9_i-sb0JUmTGgUEmQj_1kTfNp_XL7SE1lk\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAorYr1WMkcu9GqEtRAqIvEjoO-8PUIfzJ__icQifSYf51q7-i98H3YfdBtHddr1G8QAxgh6V8I7ejznbpcZUQy8ZRJiSCtd6gPkQiDoP3tZzfv58mHsnb6GHo4UDFTkjHlIwBBY7gMRlH4oitlpwvqbpyQPMal19zP8AkbmjrS_RYr4d-xJbyRpAh80N6VZ255TdsxBWu8KznvK3rqklUuCOfzcFmW71UETHYLd1m_46anWrjtN-2Yj5XPx3OhewyNcBJnjgOxNQxEIyAdxDLKaI-lhzMQl_hx5hMRHsGNvqGQnutE3h7wJKoiyRZPz1y_i7wpNoVaGnRKHSuvEK66QIDAQAB\n" + + "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\n" + + "Duzcuty33a7-g2Nphj5zhv5uJDZ-ZFkPx4Huqrp_38m1NYfgc8fShq2YE8CpODH9XsHLi3Ve2U02SQQRluPywRbRrNFtjqQxXcHU9uCUP8BaJtX-0SYdzXNWZcP5tQRfRvqhpIIpTn69keHJHqBybL4Zs9h0-0GMR2ejPEKSkInDWD5LieGyh8KnVr42HErg4d-WKQIbhopYjXAGBKOdcHoWC0L11AZxPULSGaVyvX9nGtKAerYsB-eLSWzWF3SSDSw2fI6qTsMAKwCZDunw48bPLBKc9ubU2ibayzzTanMk1EMb1PqOoHKpoOmTiThktfKlEjAdApXtE0cQXod-4w"; + + try { + String[] str = licenseInfoKey.split("\\\n"); + String privateEncrypt = str[0]; + String publicKey = str[1]; + String application = str[2]; + String sign = str[3]; + + String systemInfo = Sha1WithRsaUtil.decrypt(privateEncrypt, Sha1WithRsaUtil.getPublicKeyX509(publicKey)); + if (!LauncherValid.validLicenseCipher(systemInfo)) { + logger.error("validLicenseCipher ===> 系统签名认证错误"); + + boolean checkMachineFile = LauncherValid.checkMachineFile(null, systemInfo, machineInfoKey, false); + if (!checkMachineFile) { + logger.error("checkMachineFile ===> 系统签名认证错误"); + throw new Exception("checkMachineFile ===> 系统签名认证错误"); + } else { + logger.info("checkMachineFile ===> 系统签名认证通过"); + } + } + + String json = AESUtil.decrypt(application, systemInfo); + // 验证签名 + if (!Sha1WithRsaUtil.verify(json, sign, publicKey)) { + throw new Exception("共有签名认证错误"); + } + write(json); + License license = License.getLicense(); + System.out.println(license.toString()); + } catch (Exception e) { + e.fillInStackTrace(); + } + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/SystemInit.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/SystemInit.java new file mode 100644 index 0000000..41227df --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/launcher/SystemInit.java @@ -0,0 +1,154 @@ +package cn.crtech.cloud.licenseAnalysis.launcher; + +import cn.crtech.cloud.licenseAnalysis.utils.SystemLogger; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.List; + +/** + * License授权 系统启动工具类 + * + * @Author: TYP + * @Date: 2021-09-13 15:53 + */ + +@Slf4j +public class SystemInit { + //项目配置文件存储文件夹路径 + public static final String FILE_PATH = System.getProperty("user.dir") + File.separator + "src" + + File.separator + "main" + File.separator + "resources" + File.separator + "conf"; + + private static SystemLogger logger = new SystemLogger(SystemInit.class); + + /** + * 系统初始化 + */ + public static void init() { + // 判断对应文件夹是否存在 不存在就自动创建 + File file = new File(FILE_PATH); + if (!file.exists()) { + file.mkdirs(); + } + + log.info("================ 初始化读取电脑系统配置信息 ================"); + try { + LauncherValid.valid(); + } catch (Exception e) { + log.debug("初始化读取电脑系统配置信息失败 电脑配置文件重置完成"); + } + + log.info("================ 初始化系统License文件 ================"); + try { + LicenseValid.valid(); + } catch (Exception e) { + log.debug("初始化读取系统License文件失败"); + } + + log.info("================ 初始化系统公司配置数据 ================"); + try { + CompanyValid.valid(); + } catch (Exception e) { + log.debug("初始化读取系统公司配置数据失败"); + } + } + + /** + * 上传授权文件 + * + * @param file 文件 + */ + public static void uploadLicenseFile(File file) { + FileReader reader = null; + BufferedReader br = null; + try { + reader = new FileReader(file); + br = new BufferedReader(reader); + List keys = new ArrayList<>(); + String line = ""; + int i = 0; + while ((line = br.readLine()) != null) { + keys.add(line); + i++; + } + + if (i != 4) { + logger.error("文本信息小于4行,license文件有有误"); + throw new Exception("文本信息小于4行,license文件有有误"); + } + CompanyValid.delCompanyInfo(); + LicenseValid.initLicenseFile(keys); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (br != null) { + br.close(); + } + if (reader != null) { + reader.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * 上传授权文件 + * + * @param filePath 文件地址 + */ + public static void uploadLicenseFile(String filePath) { + FileReader reader = null; + BufferedReader br = null; + try { + reader = new FileReader(filePath); + ; + br = new BufferedReader(reader); + List keys = new ArrayList<>(); + String line = ""; + int i = 0; + while ((line = br.readLine()) != null) { + keys.add(line); + i++; + } + + if (i != 4) { + logger.error("文本信息小于4行,license文件有有误"); + throw new Exception("文本信息小于4行,license文件有有误"); + } + CompanyValid.delCompanyInfo(); + LicenseValid.initLicenseFile(keys); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (br != null) { + br.close(); + } + if (reader != null) { + reader.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * 设置企业自定义标识内容 + * + * @param companyCode 企业标识 + */ + public static void setCompanyCode(String companyCode) { + if (!CompanyValid.getHad()) { + CompanyValid.createCompanyInfo(companyCode); + } else { + logger.error("系统已有相关配置!不可重复配置!"); + } + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Application.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Application.java new file mode 100644 index 0000000..2d68130 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Application.java @@ -0,0 +1,77 @@ +package cn.crtech.cloud.licenseAnalysis.pojo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Application { + @Override + protected Application clone() { + Application tmp = new Application(); + tmp.code = this.code; + tmp.name = this.name; + tmp.version = this.version; + tmp.expiryTime = this.expiryTime; + tmp.grade = this.grade; + tmp.gradeName = this.gradeName; + tmp.popedoms = new ArrayList<>(); + for (Map popedom : this.popedoms) { + Map p = new HashMap<>(); + p.putAll(popedom); + tmp.popedoms.add(p); + } + tmp.params = new HashMap<>(); + for (Param param : this.params.values()) { + Param p = param.clone(); + tmp.params.put(p.code, p); + } + + return tmp; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public long getExpiryTime() { + return expiryTime; + } + + public String getGrade() { + return grade; + } + + public String getGradeName() { + return gradeName; + } + + public List> getPopedoms() { + return popedoms; + } + + public Map getParams() { + return params; + } + + public Param getParam(String code) { + return params.get(code); + } + + protected String code; + protected String name; + protected String version; + protected long expiryTime; + protected String grade; + protected String gradeName; + protected List> popedoms; + protected Map params; +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/License.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/License.java new file mode 100644 index 0000000..f1acf5d --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/License.java @@ -0,0 +1,141 @@ +package cn.crtech.cloud.licenseAnalysis.pojo; + +import cn.crtech.cloud.licenseAnalysis.launcher.CompanyValid; +import cn.crtech.cloud.licenseAnalysis.utils.StaticResource; +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class License { + public static boolean validLicense = false; + + public static String error; + + private String applicationGroup; + + private List applications; + + private Map company; + + private boolean dev; + + private String secret; + + private String auth_code; + + private String company_code; + + private static License license = new License(); + + public static License getLicense() { + return license.clone(); + } + + public static void init(String licenseJSON) { + Gson gson = new Gson(); + Map lm = gson.fromJson(licenseJSON,Map.class); + License.validLicense = true; + License.error = null; + License.license.applicationGroup = (String) lm.get("application_group"); + License.license.company = (Map) lm.get("company"); + License.license.applications = new ArrayList<>(); + License.license.secret = (String) lm.get("secret"); + License.license.auth_code = (String) lm.get("auth_code"); + if (lm.get("company_code") == null) { + CompanyValid.setHad(false); + } else { + License.license.company_code = (String) lm.get("company_code"); + CompanyValid.setHad(true); + } + + List> apps = (List>) lm.get("applications"); + Application a; + Param p; + for (Map app : apps) { + a = new Application(); + a.code = (String) app.get("application_code"); + a.name = (String) app.get("name"); + a.version = (String) app.get("version"); + a.expiryTime = (long) app.get("expiryTime"); + a.grade = (String) app.get("grade"); + a.gradeName = (String) app.get("gradeName"); + a.popedoms = (List>) app.get("popedoms"); + + a.params = new HashMap(); + List> params = (List>) app.get("params"); + for (Map param : params) { + p = new Param(); + p.code = (String) param.get("code"); + p.name = (String) param.get("name"); + p.value = param.get("value") + ""; + p.valueType = param.get("valueType").toString(); + p.falseDefault = param.get("falseDefault") == null ? "" : param.get("falseDefault").toString(); + p.trueDefault = param.get("trueDefault") == null ? "" : param.get("trueDefault").toString(); + p.description = (String) param.get("description"); + a.params.put(p.code, p); + } + + License.license.applications.add(a); + } + } + + @Override + protected License clone() { + License tmp = new License(); + tmp.applicationGroup = this.applicationGroup; + tmp.applications = new ArrayList<>(); + for (Application application : this.applications) { + tmp.applications.add(application.clone()); + } + tmp.company = new HashMap<>(); + tmp.dev = StaticResource.isDev(); + tmp.company.putAll(this.company); + tmp.secret = this.secret; + tmp.auth_code = this.auth_code; + tmp.company_code = this.company_code; + return tmp; + } + + public boolean isDev() { + return dev; + } + + public String getApplicationGroup() { + return applicationGroup; + } + + public List getApplications() { + return applications; + } + + public Map getCompany() { + return company; + } + + public String getSecret() { + return secret; + } + + public String getAuth_code() { + return auth_code; + } + + public String getCompanyCode() { + return company_code; + } + + private void setCode (String company_code){ + this.company_code = company_code; + } + + public static void setCompanyCode(String company_code) { + License.license.setCode(company_code); + if (company_code != null){ + CompanyValid.setHad(true); + } + } + +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Param.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Param.java new file mode 100644 index 0000000..f02c4d8 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/pojo/Param.java @@ -0,0 +1,53 @@ +package cn.crtech.cloud.licenseAnalysis.pojo; + +public class Param { + @Override + protected Param clone() { + Param tmp = new Param(); + + tmp.code = this.code; + tmp.name = this.name; + tmp.description = this.description; + tmp.value = this.value; + tmp.valueType = this.valueType; + tmp.falseDefault = this.falseDefault; + tmp.trueDefault = this.trueDefault; + return tmp; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getValue() { + return value; + } + + public String getValueType() { + return valueType; + } + + public String getFalseDefault() { + return falseDefault; + } + + public String getTrueDefault() { + return trueDefault; + } + + protected String code; + protected String name; + protected String description; + protected String value; + protected String valueType; + protected String falseDefault; + protected String trueDefault; +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/AESUtil.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/AESUtil.java new file mode 100644 index 0000000..81a94f7 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/AESUtil.java @@ -0,0 +1,100 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import org.springframework.util.Base64Utils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @version V1.0 + * @desc AES 加密工具类 + */ +public class AESUtil { + private static final String KEY_ALGORITHM = "AES"; + + /** + * 默认的加密算法 + */ + private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; + + /** + * AES 加密操作 + * + * @param content 待加密内容 + * @param password 加密密码 + * @return 返回Base64转码后的加密数据 + */ + public static String encrypt(String content, String password) { + try { + // 创建密码器 + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + byte[] byteContent = content.getBytes("utf-8"); + // 初始化为加密模式的密码器 + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password)); + // 加密 + byte[] result = cipher.doFinal(byteContent); + //通过Base64转码返回 + return Base64Utils.encodeToString(result); + } catch (Exception ex) { + Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); + } + + return null; + } + + /** + * AES 解密操作 + * + * @param content 字符串内容 + * @param password 解码秘钥 + * @return 返回操作 + */ + public static String decrypt(String content, String password) { + try { + //实例化 + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + + //使用密钥初始化,设置为解密模式 + cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); + + //执行操作 + byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content)); + String s = new String(result, "utf-8"); + return s; + } catch (Exception ex) { + ex.printStackTrace(); + Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); + } + + return null; + } + + /** + * 生成加密秘钥 + * + * @return 返回处理结果 + */ + private static SecretKeySpec getSecretKey(String password) { + //返回生成指定算法密钥生成器的 KeyGenerator 对象 + KeyGenerator kg = null; + try { + kg = KeyGenerator.getInstance(KEY_ALGORITHM); + SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); + random.setSeed(password.getBytes()); + //AES 要求密钥长度为 128 + kg.init(128, random); + //生成一个密钥 + SecretKey secretKey = kg.generateKey(); + return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为AES专用密钥 + } catch (NoSuchAlgorithmException ex) { + Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MD5.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MD5.java new file mode 100644 index 0000000..9309f19 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MD5.java @@ -0,0 +1,66 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.MessageDigest; + +public class MD5 { + private static final String key; + private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + static { + key = "aa"; + } + + public final static String md5(String s) { + if (s == null) return null; + return md5(s.getBytes()); + } + + public final static String md5(File file) { + if (file == null) return null; + if (!file.exists()) return null; + byte bytes[] = new byte[(int) file.length()]; + FileInputStream fio = null; + try { + fio = new FileInputStream(file); + fio.read(bytes); + return md5(bytes); + } catch (IOException e) { + e.printStackTrace(); + return null; + } finally { + if (fio != null) { + try { + fio.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public final static String md5(byte[] bytes) { + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char str[] = new char[j * 2]; + int k = 0; + for (int i = 0; i < j; i++) { + byte byte0 = md[i]; + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + return null; + } + } + + public final static String md5() { + return md5(key); + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MachineInfo.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MachineInfo.java new file mode 100644 index 0000000..a929314 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/MachineInfo.java @@ -0,0 +1,48 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import oshi.hardware.CentralProcessor; +import oshi.hardware.ComputerSystem; +import oshi.hardware.HWDiskStore; +import oshi.hardware.HardwareAbstractionLayer; + +import java.util.List; + +/** + * @author inaisen + */ +public class MachineInfo { + static oshi.SystemInfo si = new oshi.SystemInfo(); + + static HardwareAbstractionLayer hal = si.getHardware(); + + /** + * 主板序列号 + */ + public static String getSerialnumber() { + ComputerSystem computerSystem = hal.getComputerSystem(); + return computerSystem.getSerialNumber(); + } + + /** + * 处理器ID + */ + public static String getprocessorid() { + CentralProcessor processor = hal.getProcessor(); + return processor.getProcessorIdentifier().getProcessorID(); + } + + /** + * 硬盘SN + */ + public static String getDisksNumber() { + String disksNumber = null; + List diskStores = hal.getDiskStores(); + if (diskStores.size() > 0) { + for (HWDiskStore disk : diskStores) { + disksNumber = String.format("%s", disk.getSerial()); + break; + } + } + return disksNumber; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/Sha1WithRsaUtil.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/Sha1WithRsaUtil.java new file mode 100644 index 0000000..da36b83 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/Sha1WithRsaUtil.java @@ -0,0 +1,255 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +public class Sha1WithRsaUtil { + /** + * 加密算法RSA + */ + public static final String RSA_ALGORITHM = "RSA"; + + /** + * 签名算法 + * SHA1WithRSA MD5withRSA + */ + public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA"; + + /** + * 字符串编码 + */ + public static final String CHARSET = "UTF-8"; + + /** + * 根据RSA初始公钥 转化为 X509公钥 + * + * @param publicKey 密钥字符串(经过base64编码) + */ + public static RSAPublicKey getPublicKeyX509(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + // 通过X509编码的Key指令获得公钥对象 + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey)); + return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec); + } + + /** + * 解密操作 + * PKCS8 私钥解密(对应 X509 公钥加密)或者 X509 公钥解密(对应 PKCS8 私钥进行加密) + */ + public static String decrypt(String data, Key key) { + BigInteger modulus = keyModulus(key); + Cipher cipher = cipherInstance(Cipher.DECRYPT_MODE, key); + byte[] bytes = rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), + modulus.bitLength()); + + try { + return new String(bytes, CHARSET); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("RSA解密失败:", e); + } + } + + /** + * RSA 验签 + * + * @return 布尔值 + */ + public static boolean verify(String content, String sign, String publicKey) { + try { + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); + byte[] encodedKey = Base64.decodeBase64(publicKey); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + + signature.initVerify(pubKey); + signature.update(content.getBytes(CHARSET)); + return signature.verify(Base64.decodeBase64(sign)); + } catch (Exception e) { + throw new RuntimeException("RSA 验签失败", e); + } + } + + /** + * 分段处理 + * + * @return 返回数据集合 + */ + private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) { + int maxBlock = 0; + if (opmode == Cipher.DECRYPT_MODE) { + maxBlock = keySize / 8; + } else { + maxBlock = keySize / 8 - 11; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] buff; + byte[] resultDatas; + Exception e1; + int i = 0; + try { + while (datas.length > offSet) { + if (datas.length - offSet > maxBlock) { + buff = cipher.doFinal(datas, offSet, maxBlock); + } else { + buff = cipher.doFinal(datas, offSet, datas.length - offSet); + } + out.write(buff, 0, buff.length); + i++; + offSet = i * maxBlock; + } + resultDatas = out.toByteArray(); + } catch (Exception e) { + throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e); + } finally { + try { + out.close(); + } catch (IOException e) { + throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时,关闭流发生异常", e); + } + } + return resultDatas; + } + + /** + * 获取加密key的 实例 + */ + private static Cipher cipherInstance(int mode, Key key) { + Cipher cipher = null; + + try { + cipher = Cipher.getInstance(RSA_ALGORITHM); + cipher.init(mode, key); + } catch (Exception e) { + throw new RuntimeException("获取Cipher 实例异常:", e); + } + return cipher; + } + + private static BigInteger keyModulus(Key key) { + BigInteger modulus = null; + if (key instanceof RSAPublicKey) { + modulus = ((RSAPublicKey) key).getModulus(); + } else if (key instanceof RSAPrivateKey) { + modulus = ((RSAPrivateKey) key).getModulus(); + } + return modulus; + } + + /** + * 据说1024 加密不安全,默认 走2048加密 + */ + public static final int keySize = 2048; + + /** + * 创建RSA 原始的公钥私钥 + * + * @return 返回生成结果 + */ + public static Map createKeys() { + return createKeys(keySize); + } + + /** + * 获取初始的公钥和私钥 + * 1024 2048 + * + * @param keySize 长度 + */ + private static Map createKeys(int keySize) { + // 为RSA算法创建一个KeyPairGenerator对象 + KeyPairGenerator kpg; + try { + kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]"); + } + + // 初始化KeyPairGenerator对象,密钥长度 + kpg.initialize(keySize); + // 生成密匙对 + KeyPair keyPair = kpg.generateKeyPair(); + // 得到公钥 + Key publicKey = keyPair.getPublic(); + String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded()); + // 得到私钥 + Key privateKey = keyPair.getPrivate(); + String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded()); + + Map keyPairMap = new HashMap(); + keyPairMap.put("publicKey", publicKeyStr); + keyPairMap.put("privateKey", privateKeyStr); + return keyPairMap; + } + + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @return 签名值 + */ + public static String sign(String content, String privateKey) { + try { + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)); + + KeyFactory keyf = KeyFactory.getInstance(RSA_ALGORITHM); + PrivateKey priKey = keyf.generatePrivate(priPKCS8); + + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + + signature.initSign(priKey); + signature.update(content.getBytes(CHARSET)); + + byte[] signed = signature.sign(); + + return Base64.encodeBase64URLSafeString(signed); + } catch (Exception e) { + throw new RuntimeException("签名发生异常", e); + } + } + + /** + * 加密操作 + * X509 公钥加密(对应 PKCS8 私钥进行解密) 或者 PKCS8 私钥加密(对应X509公钥解密) + */ + public static String encrypt(String data, Key key) { + BigInteger modulus = keyModulus(key); + try { + Cipher cipher = cipherInstance(Cipher.ENCRYPT_MODE, key); + byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), + modulus.bitLength()); + + return Base64.encodeBase64URLSafeString(bytes); + } catch (Exception e) { + throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e); + } + } + + /** + * 根据RSA初始私钥 转化为 PKCS8私钥 + * + * @param privateKey 密钥字符串(经过base64编码) + */ + public static RSAPrivateKey getPrivateKeyPkcs8(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + // 通过PKCS#8编码的Key指令获得私钥对象 + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)); + RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec); + return key; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/StaticResource.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/StaticResource.java new file mode 100644 index 0000000..b60da52 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/StaticResource.java @@ -0,0 +1,36 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import java.io.File; + +/** + * desc + * + * @author: TYP + * @date: 2022-12-28 8:57 + */ +public class StaticResource { + public static final String RUN_PATH = System.getProperty("user.dir"); + + public static final String FILE_PATH = System.getProperty("user.dir") + File.separator + "src" + + File.separator + "main" + File.separator + "resources" + File.separator + "conf"; + + public static final String CONFIG_PATH = "conf"; + + public static final String WEBAPPS_PATH = "apps"; + + public static final String ADMIN_CONTEXT = "xinadmin"; + + private static final String CONFIG_FILE = "XinLauncher.ini"; + + public static final String CHIPER_FILE = "XinLauncher.data"; + + public static final String LICENSE = "license.data"; + + private static final String HOSTS_FILE = "Dev-Hosts.xml"; + + private static boolean is_dev = false; + + public static boolean isDev() { + return is_dev; + } +} diff --git a/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/SystemLogger.java b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/SystemLogger.java new file mode 100644 index 0000000..1f0a3f1 --- /dev/null +++ b/LicenseAnalysis/src/main/java/cn/crtech/cloud/licenseAnalysis/utils/SystemLogger.java @@ -0,0 +1,337 @@ +package cn.crtech.cloud.licenseAnalysis.utils; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * launcher 日志记录实体类 + * + * @Author: TYP + * @Date: 2022-09-27 11:34 + */ + +public class SystemLogger { + // 日志文件地址 + private static final String LOG_FILE_PATH = System.getProperty("user.dir") + File.separator + "logs" + File.separator; + + // 日志文件前缀名 + private static final String LOG_FILE_NAME = "XinLaucher"; + + // 日志文件编码格式 + private static final String LOG_FILE_ENCODING = "UTF-8"; + + private static final SimpleDateFormat TIME_SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private static final SimpleDateFormat DATE_SDF = new SimpleDateFormat("yyyy-MM-dd"); + + private static final int LOG_LEVEL = 3; + + private String classPath; + + public SystemLogger(Class clzz) { + this.classPath = clzz.getName(); + } + + public void info(String message) { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [INFO] ==> : " + message); + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.out.println(outMsg.toString()); + if (LOG_LEVEL >= 1) { + appendMethod(file, outMsg.toString()); + } + } + } + + public void info(String message, Object... data) { + if (data.length < 1) { + info(message); + } else { + String[] splitStr = message.split("\\{\\}"); + if (splitStr.length != data.length && splitStr.length != (data.length + 1)) { + new Exception("日志数据输出异常替换个数不一致,日志数据记录失败!").fillInStackTrace(); + } else { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [INFO] ==> : "); + + if (splitStr.length == data.length) { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i] + data[i]); + } + } else { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i]); + if (i < (splitStr.length - 1)) { + outMsg.append(data[i]); + } + } + } + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.out.println(outMsg.toString()); + if (LOG_LEVEL >= 1) { + appendMethod(file, outMsg.toString()); + } + } + } + } + } + + public void debug(String message) { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [DEBUG] ==> : " + message); + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.out.println(outMsg.toString()); + if (LOG_LEVEL >= 2) { + appendMethod(file, outMsg.toString()); + } + } + } + + public void debug(String message, Object... data) { + if (data.length < 1) { + info(message); + } else { + String[] splitStr = message.split("\\{\\}"); + if (splitStr.length != data.length && splitStr.length != (data.length + 1)) { + new Exception("日志数据输出异常替换个数不一致,日志数据记录失败!").fillInStackTrace(); + } else { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [DEBUG] ==> : "); + + if (splitStr.length == data.length) { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i] + data[i]); + } + } else { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i]); + if (i < (splitStr.length - 1)) { + outMsg.append(data[i]); + } + } + } + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.out.println(outMsg.toString()); + if (LOG_LEVEL >= 2) { + appendMethod(file, outMsg.toString()); + } + } + } + } + } + + public void error(String message) { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [ERROR] ==> : " + message); + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 3) { + appendMethod(file, outMsg.toString()); + } + } + } + + public void error(String message, Object... data) { + if (data.length < 1) { + info(message); + } else { + String[] splitStr = message.split("\\{\\}"); + if (splitStr.length != data.length && splitStr.length != (data.length + 1)) { + new Exception("日志数据输出异常替换个数不一致,日志数据记录失败!").fillInStackTrace(); + } else { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [ERROR] ==> : "); + + if (splitStr.length == data.length) { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i] + data[i]); + } + } else { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i]); + if (i < (splitStr.length - 1)) { + outMsg.append(data[i]); + } + } + } + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 3) { + appendMethod(file, outMsg.toString()); + } + } + } + } + } + + public void warn(String message) { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [WARN] ==> : " + message); + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 4) { + appendMethod(file, outMsg.toString()); + } + } + } + + public void warn(String message, Object... data) { + if (data.length < 1) { + info(message); + } else { + String[] splitStr = message.split("\\{\\}"); + if (splitStr.length != data.length && splitStr.length != (data.length + 1)) { + new Exception("日志数据输出异常替换个数不一致,日志数据记录失败!").fillInStackTrace(); + } else { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [WARN] ==> : "); + + if (splitStr.length == data.length) { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i] + data[i]); + } + } else { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i]); + if (i < (splitStr.length - 1)) { + outMsg.append(data[i]); + } + } + } + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 4) { + appendMethod(file, outMsg.toString()); + } + } + } + } + } + + public void fatal(String message) { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [FATAL] ==> : " + message); + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 5) { + appendMethod(file, outMsg.toString()); + } + } + } + + public void fatal(String message, Object... data) { + if (data.length < 1) { + info(message); + } else { + String[] splitStr = message.split("\\{\\}"); + if (splitStr.length != data.length && splitStr.length != (data.length + 1)) { + new Exception("日志数据输出异常替换个数不一致,日志数据记录失败!").fillInStackTrace(); + } else { + Date date = new Date(); + String time = TIME_SDF.format(date); + StringBuffer outMsg = new StringBuffer(time + " == [" + this.classPath + "] [FATAL] ==> : "); + + if (splitStr.length == data.length) { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i] + data[i]); + } + } else { + for (int i = 0; i <= (splitStr.length - 1); i++) { + outMsg.append(splitStr[i]); + if (i < (splitStr.length - 1)) { + outMsg.append(data[i]); + } + } + } + + String file_suffix = DATE_SDF.format(date); + String fileName = LOG_FILE_PATH + LOG_FILE_NAME + file_suffix + ".log"; + File file = checkFile(fileName); + if (file != null) { + System.err.println(outMsg.toString()); + if (LOG_LEVEL >= 5) { + appendMethod(file, outMsg.toString()); + } + } + } + } + } + + private static File checkFile(String fileName) { + File file = new File(fileName); + if (!file.exists()) { + int position = fileName.lastIndexOf(File.separator); + String packageName = fileName.substring(0, position); + File packageFile = new File(packageName); + if (!packageFile.exists()) { + packageFile.mkdirs(); + } + + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + return file; + } + + private static void appendMethod(File file, String content) { + try { + FileWriter writer = new FileWriter(file, true); + writer.write(content + "\n"); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +}