Kamixitong/app/utils/validators.py
2025-11-11 21:39:12 +08:00

232 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)