注意!Electron 無法獲取設(shè)備ID了!
大家好,我是楊成功。
在桌面應(yīng)用開發(fā)中,常常需要獲取設(shè)備唯一 ID 來表示當(dāng)前客戶端的唯一性。一般的設(shè)備 ID 需要滿足兩個條件:
- 基于硬件和系統(tǒng)配置生成,確保設(shè)備的唯一性。
- 只要不重裝系統(tǒng),設(shè)備 ID 多次獲取都是唯一的。
node-machine-id 是一個常用的 Node.js 模塊,它能夠在 Electron 中獲取機(jī)器的唯一標(biāo)識。
我們的產(chǎn)品就是使用該模塊,用法也很簡單:
import { machineIdSync } from 'node-machine-id';
let id = machineIdSync();
但是昨天出現(xiàn)了問題,排查結(jié)果是多臺設(shè)備獲取的 ID 竟然是一樣的,造成了一些設(shè)備的數(shù)據(jù)被篡改,我從 issues 中找到了一些端倪。
也就是在 Window Ghost 系統(tǒng)中會出現(xiàn)問題(啥是 Window Ghost ?)。
Window 中還經(jīng)常遇到權(quán)限問題,而且這個 ID 總歸不可控,所以還是用自定義的方式實(shí)現(xiàn)吧。
自定義設(shè)置設(shè)備 ID
自定義的設(shè)備 ID 首先需要唯一,其次在安裝和卸載應(yīng)用時設(shè)備 ID 不變。
滿足這兩個要求,最佳的方案就是將自己生成的設(shè)備 ID 存儲在用戶目錄下。
假設(shè)當(dāng)前用戶叫張三,他的用戶目錄:
- Window:C:\Users\張三\
- MacOS:/Users/張三/
很多應(yīng)用程序都把配置寫到用戶目錄下,且該目錄一般不會遇到權(quán)限問題。
使用 uuid 生成設(shè)備 ID:
import { v4 as uuidv4 } from 'uuid';
const device_id = uuidv4();
在主進(jìn)程中獲取到用戶目錄,非常簡單:
import { app } from 'electron';
const user_path = app.getPath('home'); // 自動獲取 Win 或 Mac 的用戶目錄
在用戶目錄下創(chuàng)建 .elappid 文件,存放生成的設(shè)備 ID:
import { join } from 'node:path';
import fs from 'node:fs';
// 獲取配置文件地址
let appid_path = join(user_path, '.elappid');
// 判斷文件是否存在,不存在就先創(chuàng)建,并寫入設(shè)備ID
if (!fs.existsSync(appid_path)) {
fs.writeFileSync(appid_path, device_id, 'utf8');
}
讀取設(shè)備 ID,并發(fā)送給渲染進(jìn)程:
let appid = fs.readFileSync(appid_path, 'utf8');
win.webContents.send('susr-config', { appid });
寫一個進(jìn)程間交互的方法,就能拿到設(shè)備 ID 了。
什么時候獲取設(shè)備 ID
正常情況下,我們希望用戶打開應(yīng)用的時候,主動獲取設(shè)備 ID 并發(fā)給渲染進(jìn)程。
然而經(jīng)過測試,在創(chuàng)建瀏覽器窗口的同時立即獲取設(shè)備 ID 并通知渲染進(jìn)程,在正式環(huán)境中,渲染進(jìn)程往往接受不到消息。
這是因?yàn)閯?chuàng)建窗口時,頁面還沒有初始化完成,自然接收不到消息。
保險(xiǎn)的方法就是在頁面加載完成后再獲取設(shè)備 ID,方法如下:
win = new BrowserWindow({...})
// 頁面加載完成后觸發(fā):
win.webContents.on("did-finish-load", () => {
console.log('在這里獲取設(shè)備ID吧')
})
大功告成,你也試試吧!