前言:项目中需要使用到 ukey 更新,业务代码都写好了,后面告知不需要太复杂,要删除轮巡监听 ukey 通信启动程序 的步骤,token 刷新机制
这是控制 SoftUkey 程序创建的类
- 内部调用 SoftKey3W 的方法获取ukey的设备信息
- 使用 websocket 与厂家提供的软件程序通信(独立程序.exe)
- websocket 断连重试
- 每隔10分钟自动调用一次刷新机制
ukey调用方法 | 调用类 |
---|---|
FindPort | SoftKey3W |
GetID_1 | SoftKey3W |
GetID_2 | SoftKey3W |
YReadEx | SoftKey3W |
YReadString | SoftKey3W |
GetLastError | SoftKey3W |
EncString | SoftKey3W |
StrDec | SoftKey |
调用类的方法按自己需求去模拟。也可以不去调用注释即可
舍不得删除代码,分析此片段代码。
javascript
import Emitter from 'tiny-emitter';
import { setToken, removeToken } from '@/utils/auth';
import { SoftKey, SoftKey3W, sprint } from '@/lib/ukey';
import { Message } from 'element-ui';
import dayjs from 'dayjs';
/*
export function sprint(func, t = 1000, maxStep = 100, leading = false) {
const stamp = Date.now();
let raf;
let counter = 0;
let old_counter = 0;
leading && func && func instanceof Function && func(counter);
(function loop() {
if (counter !== old_counter) {
func && func instanceof Function && func(counter);
old_counter = counter;
}
counter = Math.floor((Date.now() - stamp) / t);
if (counter >= maxStep) return;
raf = window.requestAnimationFrame(loop);
})();
function cancalAnimation() {
cancelAnimationFrame(raf);
}
return cancalAnimation;
}
*/
function GetRandomNum(Min, Max) {
var Range = Max - Min;
var Rand = Math.random();
return (Min + Math.round(Rand * Range)).toString();
}
const MAX_COUNT = 60;
const RETRY_DELAY = 2000;
const RETRY = true;
const REFRESH_TOKEN = 1000 * 60 * 10;
class MySocket extends Emitter {
ws = null;
#retry_count = MAX_COUNT;
#connected_state = null;
#timer = null;
#once_disconnected = false;
#protocol = document.URL.startsWith('https') ? 'wss://' : 'ws://';
constructor() {
super();
this.#self_connected();
}
#self_connected() {
this.#self_disconnected();
const url = `${this.#protocol}127.0.0.1:4006/xxx`;
const WindowWebSocket = typeof MozWebSocket !== 'undefined' ? MozWebSocket : WebSocket;
const ws = (this.ws = new WindowWebSocket(url, 'usbkey-protocol'));
ws.onopen = this.#onopen.bind(this);
ws.onclose = this.#onclose.bind(this);
ws.onerror = this.#onerror.bind(this);
ws.onmessage = this.#onmessage.bind(this);
}
#self_disconnected() {
if (!this.ws) return;
this.ws.close();
this.ws.onopen = null;
this.ws.onmessage = null;
this.ws.onerror = null;
this.ws.onclose = null;
this.ws = null;
}
#onopen(_event) {
clearTimeout(this.#timer);
this.#connected_state = 'connected';
this.#retry_count = MAX_COUNT;
this.emit('connected');
}
#onclose(_event) {
const retry = () => {
if (this.#retry_count <= 0 || this.#connected_state === 'connected') {
clearTimeout(this.#timer);
return;
}
this.#retry_count--;
this.#self_connected();
};
this.#connected_state = 'disconnected';
if (RETRY) {
this.#timer = setTimeout(retry, RETRY_DELAY);
}
}
#onerror(_event) {
if (!this.#once_disconnected) {
// alert('未能连接服务程序,请确定服务程序是否安装。');
this.#once_disconnected = true;
}
this.emit('error');
}
#onmessage(_event) {
const data = MySocket.parseJsonString(_event.data);
if (data.type === 'PnpEvent') {
if (data.IsIn) {
MySocket.toast({
type: 'warning',
duration: 3500,
message: `UKEY已插入,被插入的锁的路径是:${data.DevicePath}`,
});
this.emit('deviceIn');
} else {
MySocket.toast({
type: 'error',
duration: 6000,
message: `UKEY已被拨出,被拨出的锁的路径是:${data.DevicePath}`,
});
this.emit('deviceOut');
}
}
}
static parseJsonString(str) {
try {
return JSON.parse(str);
} catch (e) {
return {};
}
}
static toast(...args) {
Message(...args);
}
}
class UkeyDevice extends MySocket {
static digitArray = '0123456789abcdef'.split('');
mSoftKey3A = null;
mSoftKey = null;
chipId = null;
ukeyID = null;
password = null;
username = null;
devicePath = null;
constructor() {
super();
this.mSoftKey = new SoftKey();
this.mSoftKey3A = new SoftKey3W();
this.on('connected', this.initialize.bind(this, 'connected'));
}
initialize() {
this.#transmit();
this.#ready();
}
#transmit() {
this.mSoftKey3A.Socket_UK = this.ws;
}
async #ready() {
this.devicePath = await this.#findPort();
this.ukeyID = await this.#getUkeyId();
this.username = await this.#getUsername();
this.password = await this.#getPassword();
this.emit('ready');
}
async #findPort() {
const { mSoftKey3A } = this;
let devicePath = await mSoftKey3A.FindPort(1);
if (mSoftKey3A.GetLastError() === 0) {
this.#throwError('系统上发现有2把及以上的加密锁,请只插入要进行的加密锁。');
return;
}
devicePath = await mSoftKey3A.FindPort(0);
if (mSoftKey3A.GetLastError() !== 0) {
this.#throwError('未发现加密锁,请插入加密锁');
return;
}
return devicePath;
}
async #getUkeyId() {
const { mSoftKey3A, devicePath } = this;
let ukeyID = (this.chipId = await mSoftKey3A.GetChipID(devicePath));
if (mSoftKey3A.GetLastError() !== 0) {
const aId = await mSoftKey3A.GetID_1(devicePath);
const bId = await mSoftKey3A.GetID_2(devicePath);
ukeyID = UkeyDevice.toHex(aId) + UkeyDevice.toHex(bId);
if (mSoftKey3A.GetLastError() !== 0) {
this.#throwError(`获取ID错误,错误码是 ${mSoftKey3A.GetLastError().toString()}`);
return;
}
}
return ukeyID;
}
async #getUsername() {
const { mSoftKey3A, devicePath } = this;
const addr = 0;
const outLenBuf = await mSoftKey3A.YReadEx(addr, 1, 'ffffffff', 'ffffffff', devicePath);
const nlen = outLenBuf[0];
if (mSoftKey3A.GetLastError() !== 0) {
this.#throwError(`读取用户名长度时错误。错误码:${mSoftKey3A.GetLastError().toString()}`);
return;
}
const username = await mSoftKey3A.YReadString(addr + 1, nlen, 'ffffffff', 'ffffffff', devicePath);
if (mSoftKey3A.GetLastError() !== 0) {
this.#throwError(`读取用户名时错误。错误码:${mSoftKey3A.GetLastError().toString()}`);
return;
}
return username;
}
async #getPassword() {
const { mSoftKey3A, devicePath } = this;
const addr = 20;
const outLenBuf = await mSoftKey3A.YReadEx(addr, 1, 'ffffffff', 'ffffffff', devicePath);
const nlen = outLenBuf[0];
if (mSoftKey3A.GetLastError() !== 0) {
const ErrorCode = `读取用户密码长度时错误。错误码:${mSoftKey3A.GetLastError().toString()}`;
window.alert(ErrorCode);
throw new Error(ErrorCode);
}
const password = await mSoftKey3A.YReadString(addr + 1, nlen, 'ffffffff', 'ffffffff', devicePath);
if (mSoftKey3A.GetLastError() !== 0) {
const ErrorCode = `读取用户密码时错误。错误码:${mSoftKey3A.GetLastError().toString()}`;
window.alert(ErrorCode);
throw new Error(ErrorCode);
}
return password;
}
async encode(code) {
const { mSoftKey3A, devicePath } = this;
const encCode = await mSoftKey3A.EncString(code, devicePath);
if (mSoftKey3A.GetLastError() !== 0) {
this.#throwError(`进行加密运行算时错误,错误码为:${mSoftKey3A.GetLastError().toString()}`);
return;
}
return encCode;
}
decode(code) {
const { mSoftKey } = this;
const decCode = mSoftKey.StrDec(code, this.key);
if (!decCode) {
this.#throwError(`进行解密运行算时错误,错误码为:"错误码为 null"`);
return;
}
return new Promise(resolve => resolve(decCode));
}
reset() {
Object.assign(this, {
ukeyID: undefined,
chipId: undefined,
password: undefined,
username: undefined,
devicePath: undefined,
});
}
#throwError(errorMessage) {
window.alert(errorMessage);
console.warn(errorMessage);
// throw new Error(errorMessage);
}
static toHex(n) {
let result = '';
let start = true;
for (var i = 32; i > 0; ) {
i -= 4;
let digit = (n >> i) & 0xf;
if (!start || digit != 0) {
start = false;
result += UkeyDevice.digitArray[digit];
}
}
return result === '' ? '0' : result;
}
}
export class CreateUkeyDevice extends UkeyDevice {
#timer = null;
#now = Date.now();
#duration = REFRESH_TOKEN;
constructor(...args) {
super(args);
this.on('ready', this.#setup.bind(this));
this.on('error', this.#stopSprint.bind(this));
this.on('deviceIn', this.#ukeyDeviceIn.bind(this));
this.on('deviceOut', this.#ukeyDeviceOut.bind(this));
}
#setup() {
this.#timer = sprint(this.#authorization.bind(this), this.#duration, Infinity, true);
}
#stopSprint() {
this.#timer instanceof Function && this.#timer();
this.reset();
this.#removeToken();
}
#ukeyDeviceIn() {
console.log('设备已插入:', this);
this.initialize();
}
#ukeyDeviceOut() {
console.log('设备被拔出:', this);
this.#timer instanceof Function && this.#timer();
this.reset();
this.#removeToken();
}
async #authorization() {
const diff = dayjs(Date.now()).diff(this.#now, 'milliseconds');
console.log('定时器执行中,距离上次执行间隔:', diff);
this.#now = Date.now();
const code = await this.#getServerCode();
const token = await this.#getServerToken(code);
console.log(token);
this.#setToken(token);
}
async #getServerCode() {
const { username } = this;
username;
const code = await new Promise(resolve => setTimeout(resolve.bind(this, GetRandomNum(1, 65535) + GetRandomNum(1, 65535)), 1000));
return code;
}
async #getServerToken(code) {
return new Promise(resolve =>
setTimeout(() => {
resolve(this.encode(code));
}, 1000),
);
}
#setToken(token) {
setToken(token);
}
#removeToken() {
removeToken();
}
}
内部使用的requestAnimateFrame 执行轮巡,能在页面不显示时不被调用。