import re import hashlib from datetime import datetime, timedelta from typing import Tuple, Optional, Dict, Any class LicenseValidator: """许可证验证器""" def __init__(self, config=None): """ 初始化验证器 :param config: 配置字典 """ self.config = config or {} self.max_failed_attempts = self.config.get('MAX_FAILED_ATTEMPTS', 5) self.lockout_minutes = self.config.get('LOCKOUT_MINUTES', 10) def validate_license_key(self, license_key: str) -> bool: """ 验证卡密格式(支持XXXX-XXXX-XXXX-XXXX格式) :param license_key: 卡密字符串 :return: 是否有效 """ if not license_key: return False # 去除空格和制表符,并转为大写 license_key = license_key.strip().replace(' ', '').replace('\t', '').upper() # 检查是否为XXXX-XXXX-XXXX-XXXX格式 if '-' in license_key: parts = license_key.split('-') # 应该有4部分,每部分8个字符 if len(parts) == 4 and all(len(part) == 8 for part in parts): # 检查所有字符是否为大写字母或数字 combined = ''.join(parts) if len(combined) == 32: pattern = r'^[A-Z0-9]+$' import re return bool(re.match(pattern, combined)) return False else: # 兼容旧格式:检查长度(16-32位) if len(license_key) < 16 or len(license_key) > 32: return False # 检查字符(只允许大写字母和数字) pattern = r'^[A-Z0-9_]+$' import re return bool(re.match(pattern, license_key)) def format_license_key(self, license_key: str) -> str: """ 格式化卡密为XXXX-XXXX-XXXX-XXXX格式 :param license_key: 原始卡密 :return: 格式化后的卡密 """ if not license_key: return '' # 去除空格、制表符和连字符,并转为大写 clean_key = license_key.strip().replace(' ', '').replace('\t', '').replace('-', '').upper() # 如果长度不足32位,右补0 if len(clean_key) < 32: clean_key = clean_key.ljust(32, '0') # 如果长度超过32位,截取前32位 elif len(clean_key) > 32: clean_key = clean_key[:32] # 格式化为XXXX-XXXX-XXXX-XXXX格式 formatted_key = '-'.join([ clean_key[i:i+8] for i in range(0, len(clean_key), 8) ]) return formatted_key def check_failed_attempts(self, failed_attempts: int, last_attempt_time: datetime) -> Tuple[bool, int]: """ 检查失败尝试次数和时间 :param failed_attempts: 失败次数 :param last_attempt_time: 最后尝试时间 :return: (是否允许尝试, 剩余锁定时间(秒)) """ if failed_attempts < self.max_failed_attempts: return True, 0 # 检查锁定时间是否已过 lock_time = timedelta(minutes=self.lockout_minutes) time_passed = datetime.utcnow() - last_attempt_time if time_passed >= lock_time: return True, 0 remaining_seconds = int((lock_time - time_passed).total_seconds()) return False, remaining_seconds def validate_software_version(self, version: str) -> bool: """ 验证软件版本格式 :param version: 版本字符串 :return: 是否有效 """ if not version: return False # 语义化版本格式:主版本号.次版本号.修订号 pattern = r'^\d+\.\d+\.\d+$' return bool(re.match(pattern, version)) def compare_versions(self, version1: str, version2: str) -> int: """ 比较版本号 :param version1: 版本1 :param version2: 版本2 :return: -1(version1version2) """ try: v1_parts = [int(x) for x in version1.split('.')] v2_parts = [int(x) for x in version2.split('.')] # 补齐版本号长度 max_len = max(len(v1_parts), len(v2_parts)) v1_parts.extend([0] * (max_len - len(v1_parts))) v2_parts.extend([0] * (max_len - len(v2_parts))) for v1, v2 in zip(v1_parts, v2_parts): if v1 < v2: return -1 elif v1 > v2: return 1 return 0 except (ValueError, AttributeError): return -1 def validate_machine_code(self, machine_code: str) -> bool: """ 验证机器码格式 :param machine_code: 机器码字符串 :return: 是否有效 """ if not machine_code: return False # 机器码应该是32位大写字母和数字的组合 if len(machine_code) != 32: return False pattern = r'^[A-F0-9]+$' return bool(re.match(pattern, machine_code)) def create_verification_hash(self, data: Dict[str, Any], secret_key: str) -> str: """ 创建验证哈希 :param data: 要验证的数据字典 :param secret_key: 密钥 :return: 哈希值 """ # 按键排序确保一致性 sorted_data = sorted(data.items()) combined = '&'.join([f"{k}={v}" for k, v in sorted_data]) combined += f"&key={secret_key}" hash_obj = hashlib.sha256(combined.encode('utf-8')) return hash_obj.hexdigest() def verify_hash(self, data: Dict[str, Any], hash_value: str, secret_key: str) -> bool: """ 验证哈希值 :param data: 原始数据字典 :param hash_value: 要验证的哈希值 :param secret_key: 密钥 :return: 验证结果 """ computed_hash = self.create_verification_hash(data, secret_key) return computed_hash == hash_value def is_url_safe(self, url: str) -> bool: """ 检查URL是否安全 :param url: URL字符串 :return: 是否安全 """ if not url: return False # 基本URL格式检查 pattern = r'^https?://[^\s/$.?#].[^\s]*$' if not re.match(pattern, url): return False # 检查协议 if not url.startswith(('http://', 'https://')): return False return True def sanitize_input(self, input_str: str) -> str: """ 清理输入字符串 :param input_str: 输入字符串 :return: 清理后的字符串 """ if not input_str: return '' # 移除特殊字符 dangerous_chars = ['<', '>', '"', "'", '&', '\x00'] for char in dangerous_chars: input_str = input_str.replace(char, '') # 限制长度 return input_str[:1000] def format_license_key(license_key: str) -> str: """ 格式化卡密的便捷函数 :param license_key: 原始卡密 :return: 格式化后的卡密 """ validator = LicenseValidator() return validator.format_license_key(license_key) def validate_license_key(license_key: str) -> bool: """ 验证卡密格式的便捷函数 :param license_key: 卡密字符串 :return: 是否有效 """ validator = LicenseValidator() return validator.validate_license_key(license_key)