生成機器碼
首先需要在后端寫個獲取window或linux的機器碼,根據CPU序列號和硬盤序列號(Windows),拼接得到
/*** 操作系統的工具類*/
public class OSUtils {/*** 獲取window or linux機器碼** @return*/public static String getOSNumber() {Map<String, Object> codeMap = new HashMap<>(2);String result = "";if (isWindows()) {String processorId = getCPUSerialNumber();codeMap.put("pid", processorId);String serialNumber = getHardDiskSerialNumber();codeMap.put("snm", serialNumber);String codeMapStr = JSON.toJSONString(codeMap);String serials = Md5Utils.md5(codeMapStr, SALT);result = getSplitString(serials, "-", 8);} else {codeMap.put("hmi", getHostMachineId());codeMap.put("nwi", getContainerNetworkId());String codeMapStr = JSON.toJSONString(codeMap);String serials = Md5Utils.md5(codeMapStr, SALT);result = getSplitString(serials, "-", 8);}return result;}/*** 獲取CPU序列號** @return* @throws IOException*/public static String getCPUSerialNumber() {String next;try {Process process = Runtime.getRuntime().exec(new String[]{"wmic", "cpu", "get", "ProcessorId"});process.getOutputStream().close();Scanner sc = new Scanner(process.getInputStream());String serial = sc.next();next = sc.next();} catch (IOException e) {throw new RuntimeException("獲取CPU序列號失敗");}return next;}/*** 獲取 硬盤序列號(Windows)** @return* @throws IOException* @throws InterruptedException*/public static String getHardDiskSerialNumber() {try {Process process = Runtime.getRuntime().exec(new String[]{"wmic", "path", "win32_physicalmedia", "get", "serialnumber"});process.getOutputStream().close();Scanner sc = new Scanner(process.getInputStream());String serial = sc.next();return sc.next();} catch (IOException e) {throw new RuntimeException("獲取硬盤序列號失敗");}}/*** 獲取系統序列號(linux)** @return*/public static String getHostMachineId() {try {Path path = Paths.get("/etc/machine-id");if (Files.exists(path)) {String content = new String(Files.readAllBytes(path));if (!content.isEmpty()) {return content;}}} catch (Exception e) {System.out.println("獲取序列號失敗: " + e.getMessage());}return "unknown_host_id";}public static String getContainerNetworkId() {String interfaceName = "eth0"; // 默認網卡名稱(可配置化)try {Path addressPath = Paths.get("/sys/class/net/" + interfaceName + "/address");if (Files.exists(addressPath)) {String mac = new String(Files.readAllBytes(addressPath));return mac.replace(":", ""); // 去除冒號,簡化格式}} catch (Exception e) {e.printStackTrace();}return "unknown_mac";}private static String getSplitString(String str, String split, int length) {int len = str.length();StringBuilder temp = new StringBuilder();for (int i = 0; i < len; i++) {if (i % length == 0 && i > 0) {temp.append(split);}temp.append(str.charAt(i));}return temp.toString();}public static boolean isWindows() {return System.getProperty("os.name").toLowerCase().contains("windows");}
}
這塊寫好后再啟動程序寫打印機器碼
都寫好后就可以打包上傳服務器,在服務器內編譯生成服務器的機器碼,獲取保存下來
public static void main(String[] args) {System.out.println("機器碼:" + OSUtils.getOSNumber());// System.setProperty("spring.devtools.restart.enabled", "false");SpringApplication.run(YanfanApplication.class, args);}
生成公鑰、私鑰、授權碼
把下面這段在自己的工具類里找個地方寫,寫aes加密,機器碼+時間的,執行這個main方法,打包的時候不要打包進去,會被反編譯
public static void main(String[] args) throws Exception {// 1. 生成密鑰對KeyPair keyPair = RSAUtils.generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();// 2. 獲取密鑰字符串(模擬存儲/傳輸)String publicKeyStr = RSAUtils.getPublicKeyString(publicKey);String privateKeyStr = RSAUtils.getPrivateKeyString(privateKey);// 公鑰:// publicKeyStr = "";System.out.println("publicKeyStr: " + publicKeyStr);// 私鑰: 不要將自己的私鑰放到這里!!!!!// privateKeyStr = "";System.out.println("privateKeyStr: " + privateKeyStr);// 私鑰加密,私鑰自己保留String data = RSAUtils.encryptWithPrivateKey(OS_NUMBER + "2099-12-31 23:59:59", RSAUtils.getPrivateKey(privateKeyStr));
// String data = RSAUtils.encryptWithPrivateKey("038e-1ee5-ee15-4005-18ce-15b7-312d-78ee" + "2099-12-31 23:59:59", RSAUtils.getPrivateKey(privateKeyStr));
// String data = RSAUtils.encryptWithPrivateKey(
// "038e-1ee5-ee15-4005-18ce-15b7-312d-78ee" + "2025-06-25 23:59:59",
// RSAUtils.getPrivateKey(privateKeyStr)
// );System.out.println(data);// 公鑰解密,公鑰提供給客戶System.out.println(RSAUtils.decryptWithPublicKey(data, RSAUtils.getPublicKey(publicKeyStr)));}
私鑰自己保存好,公鑰放在服務器客戶端,授權碼放在數據庫sys_config,看下面
服務器修改客戶端公鑰
生成的公鑰需要粘到服務器的private-key
授權碼對應的密鑰
授權碼找到系統數據庫的sys_config表內
全部改好后,寫一個授權碼監聽事件,到期系統自動停止
如果想要測試本地是否執行成功的,把監聽事件關閉,但是上傳服務器的時候要打開,不然授權碼這個寫了也沒用
/*** 授權碼監聽事件*/
@Component
@Slf4j
@Order(0)
public class AuthOSValidListener implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {// 啟用授權碼功能validate();}public void validate() {// 開啟驗證機器碼new AuthOsValidUtil().verification();}@Scheduled(cron = "0 0/10 * * * ?") //10執行一次private void validateTime() {validate();}
}