当前位置: 首页 > news >正文

从密钥生成到功能限制:Electron 中 secure-electron-license-keys 的完整集成方案

secure-electron-license-keys 是一个专门为 Electron 应用设计的 npm 包,用于实现离线许可证密钥的创建、验证和管理,帮助开发者保护应用程序,确保只有拥有合法许可证的用户才能使用。以下是关于它的详细介绍:

在 Electron 应用中集成 secure-electron-license-keys 包主要涉及密钥对生成、许可证创建(服务端)和客户端验证三个核心步骤。以下是详细的集成流程:

步骤 1:准备工作

  1. 安装依赖
    在 Electron 项目中安装包:

    npm install secure-electron-license-keys --save
    
  2. 项目结构建议
    建议按以下结构组织文件(区分服务端和客户端代码):

    your-electron-app/
    ├── server/                # 服务端(生成密钥对和许可证)
    │   ├── private.pem        # 私钥(保密,不随客户端打包)
    │   └── generate-license.js # 生成许可证的脚本
    ├── src/
    │   ├── public.pem         # 公钥(客户端使用,随应用打包)
    │   ├── main.js            # Electron 主进程
    │   └── renderer/          # 渲染进程(许可证输入界面)
    

步骤 2:生成 RSA 密钥对(服务端/开发者操作)

密钥对用于加密和解密许可证,私钥仅保存在服务端,公钥内置到客户端。

创建 server/generate-keys.js 脚本:

const { generateKeyPair } = require("secure-electron-license-keys");
const path = require("path");// 生成密钥对并保存到文件
generateKeyPair({privateKeyPath: path.join(__dirname, "private.pem"),  // 私钥路径(绝对路径)publicKeyPath: path.join(__dirname, "../src/public.pem"),  // 公钥路径(客户端使用)keySize: 2048  // 密钥长度(推荐 2048 或 4096)
}).then(() => {console.log("密钥对生成成功!");
}).catch(err => {console.error("生成失败:", err);
});

运行脚本生成密钥:

node server/generate-keys.js

生成后,将 public.pem 移动到客户端代码目录(如 src/),private.pem 留在服务端并严格保密。

步骤 3:创建许可证(服务端/开发者操作)

当用户购买后,服务端使用私钥生成许可证,包含用户信息、有效期等数据。

创建 server/generate-license.js 脚本:

const { createLicense } = require("secure-electron-license-keys");
const path = require("path");
const fs = require("fs");// 自定义许可证数据
const licenseData = {userId: "user123",         // 用户唯一标识email: "user@example.com", // 用户邮箱expires: "2025-12-31",     // 过期时间(格式:YYYY-MM-DD)appVersion: "1.0.0",       // 允许的应用版本features: ["export", "cloudSync"]  // 允许的功能
};// 生成许可证
createLicense({privateKeyPath: path.join(__dirname, "private.pem"),  // 私钥路径data: licenseData
}).then(licenseKey => {// 保存许可证(可发送给用户或存储到数据库)fs.writeFileSync(path.join(__dirname, `license-${licenseData.userId}.key`),licenseKey);console.log("许可证生成成功:", licenseKey);
}).catch(err => {console.error("生成失败:", err);
});

运行脚本生成许可证:

node server/generate-license.js

生成的许可证密钥(如 license-user123.key)可通过邮件等方式发送给用户。

步骤 4:客户端集成(Electron 应用中)

客户端需要实现:许可证输入界面、验证逻辑、本地保存和启动时自动验证。

4.1 主进程配置(main.js)

在主进程中初始化许可证管理器,并提供验证接口给渲染进程:

const { app, BrowserWindow, ipcMain } = require("electron");
const { LicenseManager } = require("secure-electron-license-keys");
const path = require("path");// 初始化许可证管理器
const licenseManager = new LicenseManager({publicKeyPath: path.join(__dirname, "public.pem"),  // 公钥路径licensePath: path.join(app.getPath("userData"), "license.key")  // 本地保存路径
});// 验证许可证(供渲染进程调用)
ipcMain.handle("verify-license", async (event, licenseKey) => {try {const result = await licenseManager.verifyLicense(licenseKey);if (result.valid) {// 验证成功,保存许可证到本地await licenseManager.saveLicense(licenseKey);}return result;} catch (err) {return { valid: false, error: err.message };}
});// 启动时自动验证本地许可证
ipcMain.handle("check-saved-license", async () => {try {const savedLicense = await licenseManager.loadLicense();if (!savedLicense) return { valid: false, error: "未找到许可证" };const result = await licenseManager.verifyLicense(savedLicense);return result;} catch (err) {return { valid: false, error: err.message };}
});// 创建窗口等常规逻辑...
function createWindow() {const mainWindow = new BrowserWindow({width: 800,height: 600,webPreferences: {preload: path.join(__dirname, "preload.js"),  // 预加载脚本contextIsolation: true}});mainWindow.loadFile("src/renderer/index.html");
}app.whenReady().then(createWindow);
4.2 预加载脚本(preload.js)

暴露 IPC 接口给渲染进程:

const { contextBridge, ipcRenderer } = require("electron");contextBridge.exposeInMainWorld("licenseAPI", {verifyLicense: (key) => ipcRenderer.invoke("verify-license", key),checkSavedLicense: () => ipcRenderer.invoke("check-saved-license")
});
4.3 渲染进程界面(许可证输入与验证)

创建一个简单的 HTML 界面(src/renderer/index.html),让用户输入许可证密钥:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>许可证验证</title><style>.container { margin: 20px; }input { width: 300px; padding: 8px; margin: 10px 0; }button { padding: 8px 16px; cursor: pointer; }.status { margin-top: 10px; padding: 8px; }.valid { background: #d4edda; color: #155724; }.invalid { background: #f8d7da; color: #721c24; }</style>
</head>
<body><div class="container"><h2>请输入许可证密钥</h2><input type="text" id="licenseKey" placeholder="粘贴许可证密钥"><button onclick="verify()">验证</button><div id="status" class="status"></div></div><script>// 页面加载时检查本地是否有已保存的许可证window.addEventListener("DOMContentLoaded", async () => {const result = await window.licenseAPI.checkSavedLicense();updateStatus(result);});// 验证用户输入的许可证async function verify() {const key = document.getElementById("licenseKey").value;if (!key) {alert("请输入许可证密钥");return;}const result = await window.licenseAPI.verifyLicense(key);updateStatus(result);}// 更新状态显示function updateStatus(result) {const statusEl = document.getElementById("status");if (result.valid) {statusEl.textContent = `许可证有效!用户:${result.data.email},过期时间:${result.data.expires}`;statusEl.className = "status valid";// 验证成功后可跳转到主应用界面} else {statusEl.textContent = `验证失败:${result.error}`;statusEl.className = "status invalid";}}</script>
</body>
</html>

步骤 5:功能限制逻辑

根据许可证验证结果限制应用功能(例如在主界面中):

// 主应用界面的脚本中
async function initApp() {const licenseResult = await window.licenseAPI.checkSavedLicense();if (!licenseResult.valid) {// 未通过验证,跳转到许可证输入页window.location.href = "license.html";return;}// 根据许可证数据启用/禁用功能const features = licenseResult.data.features;if (!features.includes("export")) {document.getElementById("exportBtn").disabled = true;document.getElementById("exportBtn").title = "需要高级许可证";}
}

关键注意事项

  1. 私钥安全private.pem 绝不能随客户端打包,仅限服务端使用,泄露会导致许可证可被伪造。
  2. 公钥保护:公钥会内置到客户端,可通过代码混淆工具(如 electron-obfuscator)防止被轻易提取。
  3. 离线局限性:该包仅支持离线验证,若需远程吊销许可证,需额外添加联网检查(如定期向服务器验证许可证状态)。
  4. 版本兼容:确保 secure-electron-license-keys 版本与 Electron 版本兼容(建议使用最新稳定版)。

通过以上步骤,你的 Electron 应用将具备基本的许可证验证功能,可有效控制用户访问权限。

http://www.dtcms.com/a/319415.html

相关文章:

  • Spring Cloud系列—LoadBalance负载均衡
  • 5分钟了解OpenCV
  • 用 Enigma Virtual Box 把 Qt 程序压成单文件 EXE——从编译、收集依赖到一键封包
  • 大数据spark、hasdoop 深度学习、机器学习算法的音乐平台用户情感分析系统设计与实现
  • 多线程 future.get()的线程阻塞是什么意思?
  • Spark Memory 内存设计的核心组件
  • 2025年主流开源音视频播放项目深度解析
  • 数据结构——B-树、B+树、B*树
  • flutter-使用AnimatedDefaultTextStyle实现文本动画
  • 状压DP-子集枚举技巧
  • MySQL UNION 操作符详细说明
  • 机器视觉系统工业相机的成像原理及如何选型
  • 数据结构-哈希表(散列表)
  • 进程控制:进程的创建、终止、阻塞、唤醒、切换等生命周期管理操作
  • 基于深度学习的调制信号分类识别算法的研究生学习之旅
  • C语言sprintf、strcmp、strcpy、strcat函数详解:字符串操作的核心工具
  • Modbus转Profinet网关与西门子PLC的互联配置案例:用于永宏品牌变频器的控制实现
  • 一个基于 epoll 实现的多路复用 TCP 服务器程序,相比 select 和 poll 具有更高的效率
  • 并发编程(三)线程模型和通信
  • 【AI算法承载】海思3516DV500+IMX664方案一体机芯,开放AI算法部署二次开发
  • 蓝桥杯----数码管、按键、定时器与中断
  • PTrade详细介绍
  • 【遥感图像入门】遥感中的“景”是什么意思?
  • 深入理解 ReentrantLock和AQS底层源码
  • 专题:2025财务转型与AI赋能数字化报告|附30+份报告PDF汇总下载
  • 《深入解析缓存三大难题:穿透、雪崩、击穿及应对之道》
  • cv2.threshold cv2.morphologyEx
  • 宝塔面板配置Nacos集群
  • Plant Biotechnol J(IF=10.5)|DAP-seq助力揭示葡萄白粉病抗性机制
  • 什么是POE接口?通俗理解