admin 管理员组

文章数量: 1086759


目录

一 . 简介

二 . 功能步骤

1. 下载python环境二维码依赖包

 2. 获取机器唯一uuid代码编写(Windwos,Linux)

3. 生成二维码(利用机器的唯一码)二维码页面展示

4. 激活序列表搭建

5. 生成ak文件(扫描二维码获取机器唯一码)

6. ak文件上传验证(唯一码验证比对)

7. 平台激活序列判断

三 . 总结


一 . 简介

在日常开发中,我们经常会遇到“软件激活”这一场景。无论是商业软件的授权保护,还是内部工具的使用权限控制,激活机制都是确保软件在授权范围内运行的重要手段。通过绑定设备的唯一标识(如 UUID),我们可以实现“一机一码”的激活策略,从而有效防止非法复制与滥用。

本篇博客将以实际项目中的实现为例,介绍如何在 Windows 或 Linux 系统中:

  • 获取设备的唯一标识生成二维码;

  • 结合客户信息生成专属的激活文件;

  • 验证并激活本地软件,并可设置激活有效期。

适合有授权需求的软件开发者参考,本文使用 Python 实现,并配合二维码库生成激活文件,轻量高效。

 二 . 功能步骤

1. 下载python环境二维码依赖包

pip install  qrcode

 2. 获取机器唯一uuid代码编写(Windwos,Linux)

# 获取机器唯一uuid代码编写(Windwos,Linux)
def get_machine_uuid():
    system_name = platform.system()

    if system_name == "Linux":
        try:
            return subprocess.check_output("cat /etc/machine-id", shell=True).decode().strip()
        except Exception as e:
            return f"获取 Linux UUID 出错: {e}"

    elif system_name == "Windows":
        try:
            return subprocess.check_output("wmic csproduct get UUID", shell=True).decode().split("\n")[1].strip()
        except Exception as e:
            return f"获取 Windows UUID 出错: {e}"

    else:
        return "不支持的操作系统"

3. 生成二维码(利用机器的唯一码)二维码页面展示

# 激活二维码
@app.route("/uuid_qr")
def generate_qr():
    uuid_value = get_machine_uuid()

    # 生成二维码
    qr = qrcode.QRCode(
        version=10,  # 控制二维码的尺寸(1~40)
        error_correction=qrcode.constants.ERROR_CORRECT_L,  # 低级别容错
        box_size=10,  # 每个格子的像素大小
        border=1  # 二维码边框大小
    )
    qr.add_data(uuid_value)
    qr.make(fit=True)

    img = qr.make_image(fill="black", back_color="white")

    # 将二维码转换为字节流
    img_io = io.BytesIO()
    img.save(img_io, "PNG")
    img_io.seek(0)

    return send_file(img_io, mimetype="image/png")

4. 激活序列表搭建

# 序列激活表
class Activate_info(db.Model,TimestampMixin):
    """序列激活表"""
    __tablename__ = 't_activate_info'
    __table_args__ = {
        'mysql_engine': 'InnoDB',
        'comment': '序列激活表'
    }

    # 序列ID
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment='序列ID')
    # 用户名称
    activate_username = db.Column(db.String(255), comment='用户名称')
    # 公司
    activate_company = db.Column(db.String(255), comment='公司')
    # 国家
    activate_nation = db.Column(db.String(255), comment='国家')
    # 电话
    activate_mobile = db.Column(db.String(255), comment='电话')
    # 省
    activate_province = db.Column(db.String(255), comment='省')
    # 市
    activate_city = db.Column(db.String(255), comment='市')
    # 地址
    activate_address = db.Column(db.String(255), comment='地址')
    # 激活时间
    activate_time = db.Column(db.String(255), comment='激活时间')
    # 激活秘钥
    activate_key = db.Column(db.Text, comment='激活秘钥')
    # 激活码
    activate_encrypted_data = db.Column(db.Text, comment='激活码')
    # 激活状态
    activate_status = db.Column(db.String(255), comment='激活状态  0未激活 1激活')
    # 有效时间
    activate_valid_until = db.Column(db.String(255), comment='有效时间')

5. 生成ak文件(扫描二维码获取机器唯一码)

# 产品信息获取生成ak文件
@activate.route('/product_information', methods=['POST'])
def product_information():
    # 用户名称
    username = strip_whitespace(request.form.get('username', None))
    # 公司
    company = strip_whitespace(request.form.get('company', None))
    # 国家
    nation = strip_whitespace(request.form.get('nation', None))
    # 电话
    mobile = strip_whitespace(request.form.get('mobile', None))
    # 省
    province = strip_whitespace(request.form.get('province', None))
    # 市
    city = strip_whitespace(request.form.get('city', None))
    # 地址
    address = strip_whitespace(request.form.get('address', None))
    # 激活时间
    time = strip_whitespace(request.form.get('time', None))

    # 机器序列code
    uuid_code = strip_whitespace(request.form.get('uuid_code', None))


    # 添加有效期(例如,有效期30天)
    valid_until = strip_whitespace(request.form.get('valid_until', None))

    # valid_until = (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d %H:%M:%S')

    # 激活标识key
    key = activate_key

    # 参数构建判断是否为空
    params = [username, company, nation, mobile, province, city, address, time,uuid_code,valid_until]

    if not all(params):
        return jsonify({'code': 400, 'msg': '设备数据有未填写项'})


    # 收集所有信息到一个字典
    info = {
        'username': username,
        'company': company,
        'nation': nation,
        'mobile': mobile,
        'province': province,
        'city': city,
        'address': address,
        'time': time,
        'machine_uuid': str(uuid_code),
        'key': key,
        'date_time': str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')),
        'valid_until' : str(valid_until)
    }

    # 将信息转换为JSON字符串
    info_string = json.dumps(info, sort_keys=True)
    key = cipher_suite.encrypt(info_string.encode())

    info_block = """--------------------------------------------------------- BEGIN Information ---------------------------------------------------------
    Software description     : 平台激活序列
    Extended description     : 序列生成
    Type                     : 激活码
--------------------------------------------------------- END Information ---------------------------------------------------------"""

    activation_block = f"""--------------------------------------------------------- BEGIN BLOCK ---------------------------------------------------------
{key.decode()}
--------------------------------------------------------- END BLOCK ---------------------------------------------------------"""

    activation_block_key = f"""--------------------------------------------------------- BEGIN BLOCK KEY ---------------------------------------------------------
{key_key.decode()}
--------------------------------------------------------- END BLOCK KEY ---------------------------------------------------------"""

    # 完整的激活文件内容
    ak_file_content = f"""---------------------------------------------------------- Activation File ---------------------------------------------------------
    #PLEASE DO NOT MODIFY THIS FILE. MAKING ANY CHANGE TO THIS FILE WILL INVALIDATE YOUR LICENSE.
    Copyright (C) 2024 Your Company Name. All rights reserved.

{info_block}

{activation_block}

{activation_block_key}
"""
    # 在 static 目录下新建 ak_create 目录
    ak_create_path = os.path.join(FILE_SAVE_PATH, 'ak_create')
    os.makedirs(ak_create_path, exist_ok=True,)  # 如果目录不存在则创建

    # ak文件
    ak_name = 'activation_file_{}.ak'.format(datetime.now().strftime('%Y%m%d%H%M%S'))

    # 拼接完整的文件保存路径
    file_path = os.path.join(ak_create_path, ak_name)

    # 确保目录存在
    os.makedirs(ak_create_path, exist_ok=True)

    # 写入文件
    with open(file_path, "w",encoding='utf-8') as file:
        file.write(ak_file_content)

    return jsonify(
        {'code': 200, 'msg': 'ak激活码文件生成', 'file': 'http://{}:5000/static/ak_create/{}'.format(DB_HOST, ak_name)})

6. ak文件上传验证(唯一码验证比对)

# ak激活文件上传及验证
@activate.route('/product_upload', methods=['POST'])
def product_upload():
    def extract_activation_key(file_path):
        def clean_text(text):
            # 使用正则表达式去除上下的两条线和换行符
            cleaned_text = re.sub(r'^\s*-+\s*|\s*-+\s*$', '', text, flags=re.MULTILINE)
            return cleaned_text.strip()

        with open(file_path, 'r',encoding='utf-8') as file:
            content = file.read()
        # 使用正则表达式提取 BEGIN BLOCK 和 END BLOCK 之间的内容
        match = re.search(
            r'BEGIN BLOCK\s*(.*?)\s* END BLOCK',
            content, re.DOTALL)
        match_key = re.search(
            r'BEGIN BLOCK KEY\s*(.*?)\s* END BLOCK KEY',
            content, re.DOTALL)
        if match:
            activation_key = match.group(1).strip()
            activation_key_key = match_key.group(1).strip()
            return clean_text(activation_key), clean_text(activation_key_key)
        else:
            return "文件中没有找到激活码!"

    # 解析ak文件内容
    files = request.files.get('files', None)

    # 判断当文件存在时
    if files:

        # 在 static 目录下新建 ak_activate 目录
        ak_activate_path = os.path.join(FILE_SAVE_PATH, 'ak_activate')
        os.makedirs(ak_activate_path, exist_ok=True)  # 如果目录不存在则创建

        # 文件名
        filename = files.filename
        # 拼接文件保存路径
        file_path = os.path.join(ak_activate_path, filename)

        # 将文件保存到 ak_activate 目录下
        files.save(file_path)

        try:
            # 提取密钥和加密数据
            res = extract_activation_key(file_path)

            # 进行编码处理
            key = res[1].encode()  # 从请求中获取密钥
            encrypted_data = res[0].encode()  # 从请求中获取加密数据
            print(encrypted_data)

            # 解密
            cipher_suite = Fernet(key)
            decrypted_info = cipher_suite.decrypt(encrypted_data).decode()

            # 将解密后的 JSON 字符串转换为字典
            info_dict = json.loads(decrypted_info)
            print(info_dict)

            if not info_dict.get('valid_until'):
                return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})

            # 对秘钥进行比对,如果比对成功,数据写入,并且系统数据状态发生改变
            if info_dict.get('key') != activate_key:
                return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})

            if info_dict.get('machine_uuid') != get_machine_uuid():
                return jsonify({'code':400,'msg':'ak导入校验错误,机器编码对比错误!'})


            # 判断key跟激活码是否存在,如果存在代表该激活码已经实用,请重新获取激活码
            res = db.session.query(Activate_info).filter(Activate_info.activate_key == key,
                                                         Activate_info.activate_encrypted_data == encrypted_data,
                                                         Activate_info.activate_status == 0,
                                                         ).first()
            if res:
                return jsonify({'code': 400, 'msg': '该激活码已经使用,请重新获取激活码!'})

            activate_data = Activate_info(
                activate_username=info_dict.get('username'),
                activate_company=info_dict.get('company'),
                activate_nation=info_dict.get('nation'),
                activate_mobile=info_dict.get('mobile'),
                activate_province=info_dict.get('province'),
                activate_city=info_dict.get('city'),
                activate_address=info_dict.get('address'),
                activate_time=info_dict.get('time'),
                activate_key=key,
                activate_encrypted_data=encrypted_data,
                activate_status='1',
                activate_valid_until=info_dict.get('valid_until'),
            )
            db.session.add(activate_data)
            db.sessionmit()

            # 装饰器,监听激活码数据时间,如果该时间到期,状态更改,如果为永久则不昨改动

        except FileNotFoundError:
            return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})
        except InvalidToken:
            return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})
        except json.JSONDecodeError:
            return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})
        except Exception as e:
            print(e,flush=True)
            return jsonify({'code': 400, 'msg': 'ak导入校验错误,请导入正确ak激活序列文件!'})

        return jsonify({'code': 200, 'msg': '系统激活成功!', })
    else:
        return jsonify({'code': 400, 'msg': 'ak文件未上传!', })

7. 平台激活序列判断

# 平台激活序列判断
@activate.route('/activate_verify', methods=['GET'])
def activate_verify():
    # 获取激活码序列数据及对应状态确认系统
    res = db.session.query(Activate_info).filter(Activate_info.activate_status==1).all()
    # 标记是否有有效的激活记录
    valid_activation_found = False
    if not res:
        return jsonify({"msg": "该系统尚未激活!请登录运管中心授权!", 'code': 400})
    else:
        for i in res:
            current_time = datetime.now()
            valid_until = datetime.strptime(i.activate_valid_until, '%Y-%m-%d')
            if valid_until:
                if current_time > valid_until:
                    i.activate_status = 0
                else:
                    valid_activation_found = True
        db.sessionmit()

        if valid_activation_found:
            return jsonify({"msg": "系统已激活!", 'code': 200})
        else:
            return jsonify({"msg": "系统激活序列已过期!!", 'code': 400})

8. 接口装饰器判断(对系统接口进行请求需验证token及系统是否激活)

# --------装饰器--------
def jwt_required_with_refresh(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        current_user = get_jwt_identity()

        #获取用户信息(数据库查询)
        user_info = db.session.query(User).filter(User.username == current_user).first()
        if not user_info:
            return jsonify({'msg': '用户不存在!'}), 401  # 用户不存在,返回 401 未授权

        activations = db.session.query(Activate_info).filter(Activate_info.activate_status==1).first()

        if not activations:
            return jsonify({"msg": "该系统尚未激活!请登录运管中心授权!", 'code': 400})

        # 继续执行原函数
        return fn(*args, **kwargs)

    return wrapper

三 . 总结

在本文中,我们介绍了如何为软件开发实现设备绑定激活机制。通过获取设备的唯一标识(如UUID)并结合客户信息生成专属激活文件,确保软件只能在授权设备上运行,从而有效防止非法复制和滥用。我们还探讨了如何生成二维码来便捷地展示激活信息,并通过设置激活有效期来管理软件的使用期限。整个实现过程轻量高效,适用于需要授权保护的软件开发者,尤其是利用Python语言和二维码库来生成激活文件。

本文标签: 方案 系统 软件 python