零、前言

electron中提供的nodejs核心模块以及electron可导入的三方模块在使用上都非常方便,但在刚开始使用时候踩坑会比较多,所以也汇总了一些小bug放在了文章末尾的疑难杂症中。

一、electron框架数据加密  

crypto是nodejs的核心模块,crypto模块提供了多种加密相关的功能,例如摘要运算、加密、电子签名等,用来增强安全性

本次使用crypto为数据进行加密,在electron中通过require引入该模块

const crypto = require('crypto')

//若无法直接引入,则先安装
npm install crypto

crypto的使用上,crypto可以用于创建加密和解密的对象,使用createCipherivcreateDecipheriv函数来实现

generateSecureKey自定义封装,生成随机密钥
encrypt自定义封装,加密数据
decrypt自定义封装,解密数据
crypto.randomBytes随机生成一串随机字节数组,参数指定生成字节长度,可用于生成随机密钥或随机向量
crypto.createCipheriv根据加密方式+密钥+向量来创建加密对象
crypto.createDecipheriv根据`加密方式+密钥+向量来创建解密对象
update加密或解密函数,接收一个数据(编码格式由第二个参数指定),加解密完成后转换为指定编码格式(由第三个参数指定)
final结束加密或解密,将缓冲区中数据全部返回
const generateSecureKey = () => {
  return crypto.randomBytes(32).toString('hex') // 生成32字节的随机密钥,并转换为十六进制字符串64字节
}

function encrypt(text, secretkey) {
  // 生成随机的初始化向量
  const iv = crypto.randomBytes(16)
  const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(secretkey, 'hex'), iv)
  let encrypted = cipher.update(text, 'utf8', 'hex')
  encrypted += cipher.final('hex')
  // 通常,初始化向量也应被存储,以便之后解密时使用
  return `${iv.toString('hex')}$${encrypted}`
}

function decrypt(encryptedText, secretkey) {
  // 分割出初始化向量和加密文本
  const [ivHex, encryptedTextHex] = encryptedText.split('$')
  const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(secretkey, 'hex'), Buffer.from(ivHex, 'hex'))
  let decrypted = decipher.update(encryptedTextHex, 'hex', 'utf8')
  decrypted += decipher.final('utf8')
  return decrypted
}

旧方法中的创建加密和解密对象使用createCiphercreateDecipher来实现,在过程中只需要数据与密钥即可,不用包含初始化向量。为了提高安全性,crypto进行了迭代,增加了初始化向量概念,函数更新为reateCipherivcreateDecipheriv,并在使用时需要传入向量参数,除了增大了加密算法的随机性、也保证了相同密钥加密相同消息这种情况的安全性等等

二、electron框架数据存储

electron-store 是一个专为 Electron 应用程序设计的 Node.js 模块,它提供了一个简单易用的 API 来持久化和访问 Electron 应用的数据。它允许开发者以 JSON 文件的形式存储配置数据,并提供了一个方便的方式来读写这些数据。

localStorage 是 Web 浏览器提供的客户端存储机制,用于存储单个会话中的用户数据。相对于localstoreelectron-store可以跨会话存储,保证了数据持久性

在引入上,electron30.0.5且electron-store9.0.0的情况下,electron-store只支持ES6语法的引入,所以我们在项目中可以动态引入

在electron程序初始化完成后,动态引入electron-store

app.whenReady().then(async () => {
  importStore() //动态引入electron-store
})

//动态引入electron-store
async function importStore() {
  try {
    const { default: Store } = await import('electron-store')
    //存储测试
    store = new Store()
    store.set('key', 'value');
    console.log("store path: " + store.path);
  } catch (error) {
    console.error('Failed to import electron-store:', error)
  }
}

在引入方面需要注意:返回的值需要解构,引入electron-store返回的是一个Promise,它解析成一个Module对象。Module对象包含了被导入模块的所有导出内容,除了默认导出还会有其他内容,如果直接使用返回的对象去实例化,则会报错Store是一个无法实例化的对象

// 在 electron-store 模块内部
export default Store;
export const somethingElse = 'value';

或你可以先导出再使用'.'来访问内部内容

    const Store = await import('electron-store')
    //存储测试
    store = new Store.default()

electron-store一些可用的函数如下:

// 设置一个值
store.set('user', 'wlxwlx')

// 获取一个值
let username = store.get('user')

// 更新一个值
store.set('user', 'xlw')

// 删除一个值
store.delete('user')

// 监听数据变化
store.onDidChange('user', (newVal, oldVal) => {
    console.log('user changed', newVal, oldVal)
})

当使用set方法存储数据时,数据默认会存储到电脑用户数据中,名称默认为config.json,例如

C:\Users\23949\AppData\Roaming\electronProject\config.js

使用get方法获取时,默认从该路径的配置文件下获取数据,使用store.path查看路径也会返回该路径

当然,也可以修改存储路径和文件名,在实例化时:

const store = new Store({
  name: 'xxx',
  cwd: './' //例如当前工作目录
});

疑难杂症

1.electron-store安装后无法通过require引入

electron-store用了ES6语法的封装,所以只能通过import方法导入,import有两种方式

import Store from 'electron-store'

const Store = import('electron-store')

js中默认的文件格式是commonjs,导入模组时候通过require来实现,而ES6格式需要使用import方法导入

2.import引入store后, 实例化时提示 xx is not a constructor

Store:这是整个导出对象,它包含了模块的默认导出(如果存在的话)以及其他所有命名导出

Store.default:这是导出对象中的 default 属性,它代表了模块的默认导出。如果模块没有默认导出,这个属性将是 undefined

对于default的详细解析查阅本文第二章:数据存储 

3.导入store时,import异步导致的xx is not a constructor

import方法本身时异步的,程序在import时也会继续往下执行,如果使用正确方法进行导入,但没有等待导入完成就进行实例化,会出现报错实例化没有成功,此时逻辑上的设计需要保证实例化时已经完全导入了electron-store

4.调试工具有时候无法打开的问题

这个bug与数据加密和存储无关,属于开发过程中的小bug

在创建窗口中有时候想要打开调试工具,我的做法是加载页面后打开调试工具

layoutWindow.loadFile('renderer/layout/layout.html')
layoutWindow.webContents.openDevTools()

但是loadFile这个操作是异步的,不会等待完全开启就往下继续执行,当openDevTools执行时如果页面没有完全加载,那么调试器可能会初始化失败

解决办法:添加ready-to-show事件,等待页面加载完毕再打开调试工具,once代表只执行一次

  layoutWindow.loadFile('renderer/layout/layout.html')
  layoutWindow.once('ready-to-show', () => {
      layoutWindow.webContents.openDevTools()
  })

5.解密报错格式不正确

这个bug完全是粗心造成的

//加密解密测试
const secretkey = generateSecureKey()
console.log(secretkey);
const cryptedpwd = encrypt("wlxwlx",secretkey)
console.log("pwd crypted :"+cryptedpwd)
const pwd = decrypt(cryptedpwd,secretkey)
console.log("pwd decryted :" +pwd);


//随机生成密钥
const generateSecureKey = () => {
  return crypto.randomBytes(32).toString('hex'); // 生成32字节的随机密钥,并转换为十六进制字符串64字节
};

function encrypt(text,secretkey) {
  // 生成随机的初始化向量
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(secretkey, 'hex'), iv);
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  // 通常,初始化向量也应被存储,以便之后解密时使用
  return `${iv.toString('hex')}$${encrypted}`;
}

function decrypt(encryptedText,secretkey) {
  // 分割出初始化向量和加密文本
  const [ivHex, encryptedTextHex] = encryptedText.split('$$');
  const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(secretkey, 'hex'), Buffer.from(ivHex, 'hex'));
  let decrypted = decipher.update(encryptedTextHex, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}

报错:解密传入数据格式不正确

疑惑:在解密update中可以看到需要传入的参数是hex,而split分离出来的encryptedTextHex就是hex格式,所以哪里有问题呢

**错误原因:**检查分割出来的加密文本发现变量是一个undefined,说明分割出现了错误导致传输数据错误,再排查发现是分隔符应该是一个$,而不是两个$$,修改split参数为一个$解决掉了这个问题

修改后decrypt函数代码如下:

function decrypt(encryptedText,secretkey) {
  // 分割出初始化向量和加密文本
  const [ivHex, encryptedTextHex] = encryptedText.split('$'); //-修改处-
  const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(secretkey, 'hex'), Buffer.from(ivHex, 'hex'));
  let decrypted = decipher.update(encryptedTextHex, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}

加密解密测试输出:第一个输出为随机生成的密钥,第二个输出为加密后的密码,第三个输出为解密后密码,可以看出和原密码一致

image-20240523171444514

文章作者: keeshow
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 keeshow
默认分类 Electron
喜欢就支持一下吧