Kamixitong/app/utils/validators.py

232 lines
7.4 KiB
Python
Raw Normal View History

2025-11-11 21:39:12 +08:00
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(version1<version2), 0(version1==version2), 1(version1>version2)
"""
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)