是的,这是可能的。
我自己想要这个功能,我找到了相关的解决方案,例如付费视频教程、在线解决方案 [使用Keygen ] 和其他随机黑客,但我想要离线和免费的东西,所以我为自己/其他人创建了自己的存储库利用。这是它的工作原理。
概述
- 安装
secure-electron-license-keys-cli
. (即。npm i -g secure-electron-license-keys-cli
)。
- 通过运行创建许可证密钥
secure-electron-license-keys-cli
。这会生成public.key
,private.key
和license.data
。
- 保持
private.key
安全,但要坚持在您的 Electron 应用程序public.key
的license.data
根目录中。
- 安装
secure-electron-license-keys
. (即。npm i secure-electron-license-keys
)。
- 在您的main.js文件中,查看此示例代码并添加必要的绑定。
const {
app,
BrowserWindow,
ipcMain,
} = require("electron");
const SecureElectronLicenseKeys = require("secure-electron-license-keys");
const path = require("path");
const fs = require("fs");
const crypto = require("crypto");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
title: "App title",
webPreferences: {
preload: path.join(
__dirname,
"preload.js"
)
},
});
// Setup bindings for offline license verification
SecureElectronLicenseKeys.mainBindings(ipcMain, win, fs, crypto, {
root: process.cwd(),
version: app.getVersion(),
});
// Load app
win.loadURL("index.html");
// Emitted when the window is closed.
win.on("closed", () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null;
});
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);
// Quit when all windows are closed.
app.on("window-all-closed", () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== "darwin") {
app.quit();
} else {
SecureElectronLicenseKeys.clearMainBindings(ipcMain);
}
});
- 在您的preload.js文件中,查看示例代码并添加支持代码。
const {
contextBridge,
ipcRenderer
} = require("electron");
const SecureElectronLicenseKeys = require("secure-electron-license-keys");
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld("api", {
licenseKeys: SecureElectronLicenseKeys.preloadBindings(ipcRenderer)
});
- 查看示例React组件如何验证许可证的有效性,并在您的应用程序中采取相应措施。
import React from "react";
import {
validateLicenseRequest,
validateLicenseResponse,
} from "secure-electron-license-keys";
class Component extends React.Component {
constructor(props) {
super(props);
this.checkLicense = this.checkLicense.bind(this);
}
componentWillUnmount() {
window.api.licenseKeys.clearRendererBindings();
}
componentDidMount() {
// Set up binding to listen when the license key is
// validated by the main process
const _ = this;
window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) {
console.log("License response:");
console.log(data);
});
}
// Fire event to check the validity of our license
checkLicense(event) {
window.api.licenseKeys.send(validateLicenseRequest);
}
render() {
return (
<div>
<button onClick={this.checkLicense}>Check license</button>
</div>
);
}
}
export default Component;
你完成了!
更多细节
为了进一步解释,许可证由来自客户端(即前端)页面的请求进行验证。客户端通过此调用()向主(即后端)进程发送IPC 请求window.api.licenseKeys.send(validateLicenseRequest)
。
一旦后端进程接收到这个调用(因为我们用这个调用 () 设置了它,所以它被连接起来了SecureElectronLicenseKeys.mainBindings
),库代码会尝试license.data
用public.key
. 无论这是否成功,成功状态都会被发送回客户端页面(通过 IPC)。
如何按版本限制许可证密钥
我所解释的内容非常有限,因为它不限制您可能提供给特定用户的应用程序版本。secure-electron-license-keys-cli
包括您在生成许可证密钥以设置许可证的特定主要/次要/补丁/过期值时可能传递的标志。
如果您想允许最高 7 的主要版本,您可以运行命令来生成许可证文件,如下所示:
secure-electron-license-keys-cli --major "7"
如果您想允许最高 7 的主要版本并在 2022-12-31 到期,您可以运行以下命令生成许可证文件,如下所示:
secure-electron-license-keys-cli --major "7" --expire "2022-12-31"
如果您确实运行了这些命令,则需要更新您的客户端页面以便与它们进行比较,即:
window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) {
// If the license key/data is valid
if (data.success) {
if (data.appVersion.major <= data.major &&
new Date() <= Date.parse(data.expire)) {
// User is able to use app
} else {
// License has expired
}
} else {
// License isn't valid
}
});
存储库页面有更多选项的详细信息,但这应该为您提供您将要做的事情的要点。
限制
这并不完美,但可能会处理 90% 的用户。这不能防止:
- 有人反编译您的应用程序并制作自己的许可证以完全使用/删除许可证代码
- 有人复制许可证并将其交给另一个人
如果您要打包多个或自动化的 .exe,还需要考虑如何运行这个库,因为这些许可证文件需要包含在源代码中。我会把它留给你的创造力来解决。
额外资源/免责声明
我构建了这个问题中提到的所有secure-electron-secure-electron-template
* 存储库,如果您需要交钥匙,我还维护其中已经预先烘焙到解决方案中的许可证密钥设置。