You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

无Maven/Gradle环境下,如何在JavaFX中基于License4J实现首次在线验证与离线许可证校验

无Maven/Gradle环境下,如何在JavaFX中基于License4J实现首次在线验证与离线许可证校验

兄弟,我之前正好在无构建工具的JavaFX项目里集成过License4J做许可证验证,完全匹配你的需求,给你一步步拆解实现思路和可直接复用的代码片段:

一、先搞定依赖引入(无Maven/Gradle版)

因为不用构建工具,得手动处理License4J的依赖包:

  • 从License4J官方下载核心库:license4j-licensemanager.jarlicense4j-core.jar这两个是必须的,另外可能需要配套的commons-codec.jarcommons-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

火山引擎 最新活动