Electron+Uniapp激活码验证方案(含持久化存储)
一、整体实现逻辑
核心思路:通过Electron控制应用启动流程,首次启动时弹窗收集激活码,验证通过后存储到本地文件(持久化,关机重启不丢失);后续启动直接读取本地存储的激活码,跳过弹窗。同时,Electron将激活码作为参数传递给Uniapp打包的H5页面,供Uniapp调用。
技术依赖:Electron内置的dialog(弹窗)、fs(本地文件操作)、path(路径处理),以及Uniapp的URL参数解析能力。
二、Electron端实现(核心步骤)
2.1 项目结构说明
假设Electron项目结构如下(Uniapp打包的H5放在dist目录下):
electron-project/
├─ main.js // Electron主进程(核心逻辑所在)
├─ preload.js // 主进程与渲染进程通信桥接(可选)
├─ dist/ // Uniapp打包的H5文件
│ ├─ index.html
│ └─ ...(其他H5资源)
├─ config/ // 存储激活码的目录(自动生成)
│ └─ activation.json // 激活码存储文件
└─ package.json
2.2 主进程代码(main.js)
实现激活码弹窗、本地存储、参数传递逻辑,主进程控制应用启动流程:
const { app, BrowserWindow, dialog } = require('electron');
const fs = require('fs');
const path = require('path');
// 激活码存储路径(用户目录下,避免权限问题)
const activationPath = path.join(app.getPath('userData'), 'activation.json');
let mainWindow = null;
// 读取本地激活码
function getLocalActivationCode() {
try {
if (fs.existsSync(activationPath)) {
const data = fs.readFileSync(activationPath, 'utf8');
const { code, isVerified } = JSON.parse(data);
// 验证激活码是否有效(可替换为你的后端验证逻辑)
return isVerified ? code : null;
}
} catch (err) {
console.error('读取激活码失败:', err);
}
return null;
}
// 保存激活码到本地
function saveActivationCode(code) {
try {
// 写入激活码及验证状态(这里默认前端输入后即为有效,可加后端校验)
fs.writeFileSync(activationPath, JSON.stringify({
code,
isVerified: true,
createTime: new Date().toISOString()
}), 'utf8');
return true;
} catch (err) {
console.error('保存激活码失败:', err);
return false;
}
}
// 弹窗获取激活码
async function showActivationDialog() {
const { response, inputValue } = await dialog.showInputBox({
title: '激活应用',
message: '请输入激活码以继续使用',
placeholderText: '请输入激活码',
buttons: ['确认', '取消'],
defaultId: 0,
// 可选:限制输入长度
// inputAttributes: { maxlength: 32 }
});
// 点击确认且输入不为空
if (response === 0 && inputValue?.trim()) {
const code = inputValue.trim();
// 可选:调用后端接口验证激活码有效性
// const isLegal = await fetch('你的验证接口', { method: 'POST', body: JSON.stringify({ code }) }).then(res => res.json());
const isLegal = true; // 临时模拟验证通过
if (isLegal) {
saveActivationCode(code);
return code;
} else {
dialog.showErrorBox('激活失败', '输入的激活码无效,请重新输入');
return showActivationDialog(); // 递归弹窗,直到输入有效码
}
} else {
// 点击取消或输入为空,退出应用
app.quit();
return null;
}
}
// 创建主窗口
async function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 桥接脚本
nodeIntegration: false, // 禁用node集成,提高安全性
contextIsolation: true, // 开启上下文隔离
}
});
// 获取激活码(首次弹窗,后续读取本地)
const activationCode = getLocalActivationCode() || await showActivationDialog();
// 加载Uniapp H5页面,并携带激活码参数
const h5Url = path.join(__dirname, 'dist', 'index.html');
const urlWithParams = `${h5Url}?activationCode=${encodeURIComponent(activationCode)}`;
mainWindow.loadURL(urlWithParams);
// 窗口关闭事件
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// 应用就绪后创建窗口
app.whenReady().then(createWindow);
// 关闭所有窗口后退出(macOS除外)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// macOS激活应用时重新创建窗口
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
2.3 预加载脚本(preload.js,可选)
若Uniapp需要通过Electron API二次获取激活码(而非URL参数),可通过预加载脚本暴露接口:
const { contextBridge } = require('electron');
const fs = require('fs');
const path = require('path');
const activationPath = path.join(require('electron').app.getPath('userData'), 'activation.json');
// 向Uniapp暴露获取激活码的方法
contextBridge.exposeInMainWorld('electronApi', {
getActivationCode: () => {
try {
if (fs.existsSync(activationPath)) {
const data = fs.readFileSync(activationPath, 'utf8');
return JSON.parse(data).code;
}
} catch (err) {
console.error('获取激活码失败:', err);
}
return null;
}
});
2.4 打包配置(package.json)
确保打包时包含Uniapp的H5资源,需配置build字段(需安装electron-builder):
{
"name": "your-app-name",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
},
"build": {
"appId": "com.your.app",
"productName": "你的应用名称",
"directories": {
"output": "release" // 打包输出目录
},
"files": [
"main.js",
"preload.js",
"dist/**/*" // 包含Uniapp H5资源
],
"win": {
"target": "nsis", // 打包为exe安装包
"icon": "icon.ico" // 应用图标
}
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.1"
}
}
三、Uniapp端实现(获取激活码)
Uniapp可通过两种方式获取激活码,根据需求选择:
3.1 方式一:解析URL参数(推荐,简单直接)
Electron加载H5时已将激活码拼接到URL后,Uniapp在页面初始化时解析参数即可:
// 在Uniapp的App.vue或首页onLoad生命周期中获取
export default {
onLaunch() {
// 获取URL参数
const urlParams = this.getUrlParams(window.location.href);
const activationCode = urlParams.activationCode;
if (activationCode) {
// 存储激活码(可存入vuex、uni.setStorage等)
uni.setStorageSync('activationCode', activationCode);
console.log('获取到激活码:', activationCode);
// 后续业务逻辑(如接口请求携带激活码)
} else {
console.error('未获取到激活码,应用可能未激活');
// 可选:提示用户并退出(Electron已做校验,此为兜底)
}
},
methods: {
// 解析URL参数的工具方法
getUrlParams(url) {
const params = {};
const index = url.indexOf('?');
if (index !== -1) {
const query = url.slice(index + 1);
query.split('&').forEach(item => {
const [key, value] = item.split('=');
if (key && value) {
params[key] = decodeURIComponent(value);
}
});
}
return params;
}
}
}
3.2 方式二:通过Electron预加载接口获取
若URL参数方式不满足需求,可通过预加载脚本暴露的electronApi获取:
// 在Uniapp的App.vue中
export default {
onLaunch() {
// 确保electronApi已暴露
if (window.electronApi) {
const activationCode = window.electronApi.getActivationCode();
if (activationCode) {
uni.setStorageSync('activationCode', activationCode);
console.log('通过Electron API获取激活码:', activationCode);
} else {
console.error('未获取到激活码');
}
} else {
console.error('Electron API未加载');
}
}
}
四、关键注意事项
4.1 激活码验证安全(可选)
当前方案默认输入激活码即为有效,实际项目中建议增加后端验证:
-
Electron弹窗获取激活码后,调用后端接口校验合法性;
-
验证通过后,可存储激活码+过期时间(若需时效控制);
-
后续启动时,除读取本地激活码,还需校验是否过期(可结合后端接口)。
4.2 本地文件权限问题
激活码存储路径使用app.getPath('userData'),该路径为用户专属目录(如Windows:C:\Users\用户名\AppData\Roaming\应用名),无需管理员权限,避免写入失败。
4.3 Uniapp打包配置
Uniapp打包H5时,需设置基础路径为相对路径(在manifest.json中设置:"h5": {"router": {"base": "./"}}),确保Electron能正常加载本地H5文件。
4.4 应用更新后激活码保留
由于激活码存储在用户目录下,而非应用安装目录,应用更新(重新打包安装)后,激活码仍会保留,无需重新输入。
五、测试流程
-
Uniapp打包H5,将dist目录复制到Electron项目根目录;
-
执行
npm start启动Electron应用,首次启动弹窗要求输入激活码; -
输入激活码后,确认激活成功,应用加载H5并获取参数;
-
关闭应用,重启电脑,再次启动应用,无弹窗,直接加载H5并携带激活码;
-
执行
npm run build打包为exe,安装后测试上述流程,验证功能正常。
