前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Electron开发之简单的激活码机制

Electron开发之简单的激活码机制

作者头像
码客说
发布2022-02-28 13:51:11
2.4K2
发布2022-02-28 13:51:11
举报
文章被收录于专栏:码客码客

前言

使用Electron开发客户端的时候,我们可以做在线激活,也可以做本地激活,这里我们就先实现一个本地激活的方式。

获取MAC地址

安装依赖

代码语言:javascript
复制
npm install getmac --save

代码

代码语言:javascript
复制
const getMac = require("getmac").default;
let clientId = getMac();
console.log("本机mac地址:", clientId);

获取到的结果如下

本机mac地址: 00:ff:63:f9:b4:ef

加解密

AES加解密

添加依赖

代码语言:javascript
复制
npm install crypto-js --save

aes_ecb.js

代码语言:javascript
复制
const CryptoJS = require("crypto-js");

let keystr = "JIANXINGZHEPSVMC";

// 字符串转hex
let string_to_hex = function(str) {
  let tempstr = "";
  for (let i = 0; i < str.length; i++) {
    if (tempstr === "") tempstr = str.charCodeAt(i).toString(16);
    else tempstr += str.charCodeAt(i).toString(16);
  }
  return tempstr;
};

function encStr(src) {
  let key = string_to_hex(keystr);

  key = CryptoJS.enc.Hex.parse(key);
  const enc = CryptoJS.AES.encrypt(src, key, {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
  });

  return enc.ciphertext.toString();
}

function decryptStr(enced) {
  let key = string_to_hex(keystr);

  key = CryptoJS.enc.Hex.parse(key);
  const dec = CryptoJS.AES.decrypt(CryptoJS.format.Hex.parse(enced), key, {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
  });

  return CryptoJS.enc.Utf8.stringify(dec);
}

export { encStr, decryptStr };

调用

代码语言:javascript
复制
import {encStr} from "./utils/aes_ecb";
let str = encStr("123456");

MD5加密

方式1

md5.js

代码语言:javascript
复制
const CryptoJS = require("crypto-js");
const md5 = str => CryptoJS.MD5(str).toString();
export { md5 };

使用

代码语言:javascript
复制
import { md5 } from "./utils/md5";
let str = md5("123456");

方式2

安装md5加密

代码语言:javascript
复制
npm install md5-node --save

测试

代码语言:javascript
复制
const md5 = require("md5-node");
console.log(md5('123456'));

激活码算法

预激活码

预激活码是用户提供的,用户发送给激活人员,生成激活码后再把激活码发给用户。

因为没有云服务器,所以我们必须用户能提供必要的信息,这里主要需要用户的MAC地址激活的天数

代码语言:javascript
复制
const getMac = require("getmac").default;
import { encStr, decryptStr } from "./utils/aes_ecb";

let clientId = getMac();
console.log("本机mac地址:", clientId);

let actmsg = {
  name: "码客说",
  email: "183518918@qq.com",
  mac: clientId,
  days: 10
};

let str_enc = encStr(JSON.stringify(actmsg));
console.info("加密后:" + str_enc);

let str_dec = decryptStr(str_enc);
console.info("解密后:" + str_dec);

可以看到结果

本机mac地址: 00:ff:63:f9:b4:ef 加密后:b9fd9ea920a9b236603e182b1b642779fc76300f508d5c5883dc7c1228e500d3c7631f52730ee450ecb1e4bf1a71d533770944250bff0f91f438cd11275c5424986ef9c2c 6e7107b00a4032bc5304f82036dfca05b9c3874f6e7b89dcd82de8e 解密后:{“name”:”码客说”,”email”:”183518918@qq.com“,”mac”:”00:ff:63:f9:b4:ef”,”days”:10}

这样我们需要用户把加密后的字符串发送过来就可以了。我们就可以获取到用户的基本信息了,其中name和email是用来辨别身份的,和激活码的生成无关。

生成激活码

代码语言:javascript
复制
let time_begin = Date.parse(new Date()) / 1000;
console.info("开始时间:" + time_begin);

let time_end = Date.parse(new Date()) / 1000 + 10 * 24 * 60 * 60;
console.info("结束时间:" + time_end);

let actmsg2 = {
  name: "码客说",
  email: "183518918@qq.com",
  mac: clientId,
  days: 10,
  time_begin: time_begin,
  time_end: time_end
};
let str_enc2 = encStr(JSON.stringify(actmsg2));
console.info("加密后:" + str_enc2);

let str_dec2 = decryptStr(str_enc2);
console.info("解密后:" + str_dec2);

结果

开始时间:1645938395 结束时间:1646802395 加密后:b9fd9ea920a9b236603e182b1b642779fc76300f508d5c5883dc7c1228e500d3c7631f52730ee450ecb1e4bf1a71d533770944250bff0f91f438cd11275c5424986ef9c2c 6e7107b00a4032bc5304f824eb4e57bc89ff8310de921754e0077f6aaa6833d2ef4df743a9b4724b62952b3809135a6f4617db05c3d550294fcadb7efead132446d947aac6f99ca54 896c34 解密后:{“name”:”码客说”,”email”:”183518918@qq.com“,”mac”:”00:ff:63:f9:b4:ef”,”days”:10,”time_begin”:1645938395,”time_end”:1646802395}

我们就可以把加密后的字符串作为激活码发送给用户了。

这样这个激活码不但限制了使用的设备,还限制了使用的时间。

日期加密

上面其实已经可以了,但是我们必须再开发一个专门生成激活码的应用,这里图省事,就直接把激活码生成器放在了程序中,但是不能让用户也能生成,这里就设计了一个加密的算法,每隔一个小时自动变更,这样我们可以输入日期密码,进入激活码的生成页面。

date_enc.js

代码语言:javascript
复制
import { md5 } from "./md5";

Date.prototype.format = function(fmt) {
  var o = {
    "M+": this.getMonth() + 1, //月份
    "d+": this.getDate(), //日
    "h+": this.getHours(), //小时
    "m+": this.getMinutes(), //分
    "s+": this.getSeconds(), //秒
    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
    S: this.getMilliseconds() //毫秒
  };
  if (/(y+)/.test(fmt))
    fmt = fmt.replace(
      RegExp.$1,
      (this.getFullYear() + "").substr(4 - RegExp.$1.length)
    );
  for (var k in o)
    if (new RegExp("(" + k + ")").test(fmt))
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
      );
  return fmt;
};

function date_enc() {
  var miwen1 = new Date().format("yyyyMMddhh");

  var mystr = miwen1
    .split("")
    .reverse()
    .join("");
  mystr = "xhkj" + mystr;
  mystr = md5(mystr);
  if (mystr.length > 6) {
    mystr = mystr.substring(0, 6);
  }
  return mystr;
}

export { date_enc };

调用

代码语言:javascript
复制
import { date_enc } from "./utils/date_enc";
console.info("日期加密:" + date_enc());

系统对接

这种激活方法的弊端在于用户如果修改本地时间就可以一直使用。

激活页面

image-20220227152326540
image-20220227152326540

获取激活码页面

image-20220227152417454
image-20220227152417454

渲染进程页面

代码语言:javascript
复制
<div>
    <Button
            type="primary"
            @click="jihuo_tool_dialog_click"
            style="margin-right: 10px;"
            >获取激活码
    </Button>
    <Button
            type="primary"
            @click="jihuo_dialog_click"
            style="margin-right: 10px;"
            >激活
    </Button>
</div>
<Modal v-model="show_jihuo" title="激活" ok-text="激活" @on-ok="jihuo_ok">
    <Form :model="jihuo_obj" :label-width="80">
        <FormItem label="姓名">
            <Input v-model="jihuo_obj.name" placeholder="姓名" />
        </FormItem>
        <FormItem label="邮箱">
            <Input v-model="jihuo_obj.email" placeholder="邮箱" />
        </FormItem>
        <FormItem label="天数">
            <Input v-model="jihuo_obj.days" placeholder="天数" type="number" />
        </FormItem>
        <FormItem label="">
            <Button @click="genate_yujihuo">生成预激活码</Button>
        </FormItem>
        <FormItem label="预激活码">
            <Input
                   v-model="jihuo_obj.source"
                   placeholder="预激活码"
                   type="textarea"
                   />
        </FormItem>
        <Divider>激活</Divider>
        <FormItem label="激活码">
            <Input
                   v-model="jihuo_obj.code"
                   type="textarea"
                   placeholder="请输入激活码"
                   />
        </FormItem>
    </Form>
</Modal>

<Modal v-model="show_jihuo_tool" title="获取激活码" :footer-hide="true">
    <Form :model="jihuo_obj" :label-width="80">
        <FormItem label="预激活码">
            <Input
                   v-model="jihuo_obj.source"
                   placeholder="预激活码"
                   type="textarea"
                   />
        </FormItem>

        <FormItem label="密钥">
            <Input v-model="jihuo_obj.key" placeholder="密钥" />
        </FormItem>

        <FormItem label="">
            <Button @click="get_act_code">获取激活码</Button>
        </FormItem>
        <Divider>激活码</Divider>
        <FormItem label="激活码">
            <Input
                   v-model="jihuo_obj.code"
                   type="textarea"
                   placeholder="请输入激活码"
                   />
        </FormItem>
    </Form>
</Modal>

渲染进程JS

代码语言:javascript
复制
const { ipcRenderer } = window.require("electron");

export default {
    name: "App",
    data: function() {
        return {
            is_activation: false,
            show_jihuo: false,
            show_jihuo_tool: false, //获取激活码
            jihuo_obj: {
                name: "",
                email: "",
                days: 7,
                source: "",
                key: "",
                code: ""
            }
        };
    },
    mounted() {
        ipcRenderer.on("get_code_pre_result", (event, data) => {
            if (data) {
                this.jihuo_obj.source = data;
            }
        });

        ipcRenderer.on("get_code_result", (event, data) => {
            if (data) {
                this.jihuo_obj.code = data;
            }
        });

        ipcRenderer.on("activation_result", (event, data) => {
            if (data) {
                console.info("激活信息:", data);
                localStorage.setItem("activation", data);
                this.$Message.success("激活成功");
                this.is_activation = true;
            }
        });

        ipcRenderer.on("error_msg", (event, data) => {
            if (data) {
                this.$Message.error(data);
            }
        });

        this.is_activation_action();
        if (!this.is_activation) {
            this.show_jihuo = true;
        }

        if (window.myinter) {
            clearInterval(window.myinter);
        }
        window.myinter = setInterval(() => {
            this.is_activation_action();
        }, 10000);
    },
    methods: {
        is_activation_action() {
            console.info("获取激活状态");
            let activation = localStorage.getItem("activation");
            if (activation) {
                let activation_obj = JSON.parse(activation);
                let { time_begin, time_end } = activation_obj;
                let time_now = Date.parse(new Date()) / 1000;
                if (time_now >= time_begin && time_now <= time_end) {
                    this.is_activation = true;
                } else {
                    this.is_activation = false;
                }
            } else {
                this.is_activation = false;
            }
        },
        jihuo_tool_dialog_click() {
            this.show_jihuo_tool = true;
        },
        jihuo_dialog_click() {
            this.show_jihuo = true;
        },
        genate_yujihuo() {
            if (!this.jihuo_obj.name) {
                this.$Message.error("姓名不能为空!");
                return;
            }

            if (!this.jihuo_obj.email) {
                this.$Message.error("邮箱不能为空!");
                return;
            }

            if (this.jihuo_obj.days <= 0) {
                this.$Message.error("天数有误!");
                return;
            }
            let { name, email, days } = this.jihuo_obj;
            let obj = { name, email, days };
            ipcRenderer.send("get_code_pre", obj);
        },
        get_act_code() {
            let { source, key } = this.jihuo_obj;
            if (!source) {
                this.$Message.error("预激活码不能为空!");
                return;
            }

            if (!key) {
                this.$Message.error("密钥不能为空!");
                return;
            }

            ipcRenderer.send("get_code", {
                source,
                key
            });
        },
        jihuo_ok() {
            if (!this.jihuo_obj.code) {
                this.$Message.error("激活码不能为空!");
                return;
            }
            ipcRenderer.send("activation_code", this.jihuo_obj.code);
        }
    }
};

主进程中

代码语言:javascript
复制
// 获取预激活码
ipcMain.on("get_code_pre", (event, arg) => {
  let { name, email, days } = arg;
  let clientId = getMac();
  console.log("本机mac地址:", clientId);
  let actmsg = {
    name: name,
    email: email,
    mac: clientId,
    days: days
  };

  let str_enc = encStr(JSON.stringify(actmsg));
  event.reply("get_code_pre_result", str_enc);
});

// 获取激活码
ipcMain.on("get_code", (event, arg) => {
  try {
    let { source, key } = arg;
    let date_str = date_enc();
    if (key !== date_str) {
      event.reply("error_msg", "密匙不正确!");
      return;
    }

    let str_dec = decryptStr(source);
    let { name, email, mac, days } = JSON.parse(str_dec);

    let time_begin = Date.parse(new Date()) / 1000;
    let time_end = Date.parse(new Date()) / 1000 + days * 24 * 60 * 60;
    let actmsg2 = {
      name: name,
      email: email,
      mac: mac,
      days: days,
      time_begin: time_begin,
      time_end: time_end
    };
    let str_enc2 = encStr(JSON.stringify(actmsg2));
    event.reply("get_code_result", str_enc2);
  } catch (e) {
    event.reply("error_msg", "获取激活码失败!");
  }
});

// 激活
ipcMain.on("activation_code", (event, arg) => {
  try {
    let str_dec = decryptStr(arg);
    console.info("解密后:" + str_dec);
    let obj = JSON.parse(str_dec);
    if (obj.time_begin && obj.time_end) {
      event.reply("activation_result", str_dec);
    } else {
      event.reply("error_msg", "激活码无效!");
    }
  } catch (e) {
    event.reply("error_msg", "激活码无效!");
  }
});

预激活码

代码语言:javascript
复制
713bfee46b6bfabfd8923769b2882daca6e49e97ee94b1d8daef22d361377fb9c26d4f39871c1ebcd7eeddf84fa2c3e53e93de900521906ceeb3ec9b7e439957dbdf5cfd6c80142e5baf9a2ce10ef3e8

激活码

代码语言:javascript
复制
713bfee46b6bfabfd8923769b2882daca6e49e97ee94b1d8daef22d361377fb9c26d4f39871c1ebcd7eeddf84fa2c3e53e93de900521906ceeb3ec9b7e439957d9612210261f11393adc81eca995735cc94fef4be5c6f2cd7489b5b7a49589e65cda876dffa58bfc640f713b8bf23e5801b641e63cf111c1f8d3405800d8c0b2
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 获取MAC地址
  • 加解密
    • AES加解密
      • MD5加密
        • 方式1
        • 方式2
    • 激活码算法
      • 预激活码
        • 生成激活码
          • 日期加密
          • 系统对接
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档