无Maven/Gradle环境下,如何在JavaFX中基于License4J实现首次在线验证与离线许可证校验
无Maven/Gradle环境下,如何在JavaFX中基于License4J实现首次在线验证与离线许可证校验
兄弟,我之前正好在无构建工具的JavaFX项目里集成过License4J做许可证验证,完全匹配你的需求,给你一步步拆解实现思路和可直接复用的代码片段:
一、先搞定依赖引入(无Maven/Gradle版)
因为不用构建工具,得手动处理License4J的依赖包:
- 从License4J官方下载核心库:
license4j-licensemanager.jar、license4j-core.jar这两个是必须的,另外可能需要配套的commons-codec.jar、commons-logging.jar这些基础依赖 - 把这些jar包放到项目的
lib目录下,然后在IDE里添加为项目库(比如IntelliJ是File -> Project Structure -> Libraries,点+选中这些jar包即可)
二、核心实现步骤
1. 启动时判断是否需要激活
主应用启动时,先检查本地有没有合法的许可证存档,没有就弹出激活窗口:
@Override public void start(Stage primaryStage) { // 检查本地许可证是否有效 if (isLocalLicenseValid()) { // 直接进入主应用 launchMainApplication(primaryStage); } else { // 弹出激活窗口 showLicenseActivationDialog(); } }
2. 许可证输入弹窗(JavaFX实现)
做一个模态弹窗,让用户输入密钥并触发在线验证,界面简单直接就行:
private void showLicenseActivationDialog() { Stage dialog = new Stage(); dialog.initModality(Modality.APPLICATION_MODAL); dialog.setTitle("激活你的应用"); // 布局元素 TextField licenseKeyInput = new TextField(); licenseKeyInput.setPromptText("请输入许可证密钥"); Button validateBtn = new Button("在线验证激活"); validateBtn.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white;"); // 验证按钮点击事件 validateBtn.setOnAction(e -> { String inputKey = licenseKeyInput.getText().trim(); if (inputKey.isEmpty()) { showAlert("提示", "请输入有效的许可证密钥"); return; } // 调用在线验证逻辑 if (validateLicenseOnline(inputKey)) { // 验证成功,保存许可证到本地 saveLicenseToLocal(inputKey); dialog.close(); showAlert("激活成功", "欢迎使用本应用!"); // 启动主应用 launchMainApplication(new Stage()); } else { showAlert("激活失败", "密钥无效或网络异常,请检查后重试"); } }); // 组装布局 VBox root = new VBox(15, new Label("请输入你的许可证密钥"), licenseKeyInput, validateBtn); root.setPadding(new Insets(25)); root.setAlignment(Pos.CENTER); dialog.setScene(new Scene(root, 450, 220)); dialog.showAndWait(); } // 简易提示弹窗 private void showAlert(String title, String content) { Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle(title); alert.setHeaderText(null); alert.setContentText(content); alert.showAndWait(); }
3. 在线验证License4J逻辑
用License4J的LicenseManager对接你的验证服务器(不管是License4J官方托管的还是自己部署的),同时校验节点锁定和有效期:
private boolean validateLicenseOnline(String licenseKey) { try { LicenseManager licenseManager = new LicenseManager(); // 配置你的验证服务器地址 String validationServerUrl = "http://你的验证服务器地址/validate"; // 获取本地硬件标识,确保节点锁定 String machineId = getMachineHardwareId(); // 发起在线验证 LicenseValidationResult result = licenseManager.validateLicense(licenseKey, validationServerUrl, machineId); // 校验核心条件:许可证有效、节点匹配、未过期 if (result.isValid() && result.isNodeLocked() && result.getExpirationDate().after(new Date())) { return true; } else { System.out.println("验证失败原因:" + result.getFailureReason()); return false; } } catch (Exception ex) { ex.printStackTrace(); return false; } } // 获取机器唯一标识(这里用MAC地址示例,也可以用CPU ID等) private String getMachineHardwareId() throws SocketException { Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface ni = networkInterfaces.nextElement(); byte[] mac = ni.getHardwareAddress(); if (mac != null && mac.length == 6) { StringBuilder macStr = new StringBuilder(); for (byte b : mac) { macStr.append(String.format("%02X-", b)); } return macStr.deleteCharAt(macStr.length() - 1).toString(); } } return "UNKNOWN_MACHINE"; }
4. 本地保存许可证
验证成功后,把许可证密钥(或者序列化的License对象)存到用户目录下的隐藏文件夹,避免被误删:
private void saveLicenseToLocal(String licenseKey) { try { // 用用户目录下的隐藏文件夹存储 Path licenseDir = Paths.get(System.getProperty("user.home") + "/.myapp_license"); if (!Files.exists(licenseDir)) { Files.createDirectories(licenseDir); } Path licenseFile = licenseDir.resolve("license.dat"); // 生产环境建议加密后再存储,这里示例用明文 Files.writeString(licenseFile, licenseKey, StandardOpenOption.CREATE_NEW); } catch (IOException ex) { ex.printStackTrace(); showAlert("错误", "保存许可证失败,请检查文件权限"); } }
5. 离线校验逻辑(后续启动用)
下次启动时,直接读取本地许可证,用License4J的离线验证功能校验,不用联网:
private boolean isLocalLicenseValid() { Path licenseFile = Paths.get(System.getProperty("user.home") + "/.myapp_license/license.dat"); if (!Files.exists(licenseFile)) { return false; } try { String savedKey = Files.readString(licenseFile); LicenseManager licenseManager = new LicenseManager(); // 配置你的License4J公钥,用于离线验证签名合法性 licenseManager.setPublicKey("你的License4J公钥字符串"); // 离线验证 LicenseValidationResult result = licenseManager.validateLicenseOffline(savedKey); // 再次校验节点锁定和有效期 return result.isValid() && result.isNodeLocked() && result.getExpirationDate().after(new Date()) && result.getNodeIdentifier().equals(getMachineHardwareId()); } catch (Exception ex) { ex.printStackTrace(); return false; } }
6. 主应用启动逻辑
这个就是你原本的主界面代码,比如:
private void launchMainApplication(Stage primaryStage) { primaryStage.setTitle("我的JavaFX桌面应用"); primaryStage.setScene(new Scene(new Label("主应用界面 - 已激活"), 800, 600)); primaryStage.show(); }
三、生产环境注意事项
- 加密本地许可证:示例用明文存储密钥,生产环境建议用AES加密后再写入文件,避免被篡改
- 硬件标识稳定性:尽量用多个硬件信息拼接成唯一标识,避免单个硬件更换导致验证失败
- 异常处理:在线验证时要处理网络超时、服务器不可达等异常,给用户更友好的提示
备注:内容来源于stack exchange,提问作者Minari BLIИK




