# =========================================== # 班级操行分管理系统 - 后端服务 # # 开发者: Canglan # 联系方式: admin@sea-studio.top # 版权归属: Sea Network Technology Studio # 许可证: MIT License # # 版权所有 © Sea Network Technology Studio # =========================================== import hashlib import secrets import re from config import settings class SecurityUtils: """安全工具类""" @staticmethod def sha1_md5_password(password: str) -> str: """ 双重加密:sha1 + md5 流程:原始密码 -> sha1 -> 加盐 -> md5 """ # 第一层:SHA1 sha1_hash = hashlib.sha1(password.encode('utf-8')).hexdigest() # 加盐 salted = sha1_hash + settings.PASSWORD_SALT # 第二层:MD5 md5_hash = hashlib.md5(salted.encode('utf-8')).hexdigest() return md5_hash @staticmethod def verify_password(plain_password: str, hashed_password: str) -> bool: """验证密码""" return SecurityUtils.sha1_md5_password(plain_password) == hashed_password @staticmethod def generate_random_password(length: int = 8) -> str: """生成随机密码""" alphabet = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789' return ''.join(secrets.choice(alphabet) for _ in range(length)) @staticmethod def validate_password_strength(password: str) -> tuple: """ 验证密码强度 要求:大小写字母、数字、特殊符号 至少包含其中3种 返回: (是否有效, 错误信息) """ if len(password) < 6: return False, "密码长度至少6位" if len(password) > 20: return False, "密码长度不能超过20位" # 检查四种字符类型 has_upper = any(c.isupper() for c in password) # 大写字母 has_lower = any(c.islower() for c in password) # 小写字母 has_digit = any(c.isdigit() for c in password) # 数字 has_special = any(not c.isalnum() for c in password) # 特殊符号 # 统计满足的字符类型数量 char_types = sum([has_upper, has_lower, has_digit, has_special]) if char_types < 3: return False, "密码必须包含大写字母、小写字母、数字、特殊符号中的至少3种" return True, "" @staticmethod def sanitize_string(value: str, max_length: int = 255) -> str: """ 清理字符串输入 - 去除首尾空格 - 限制长度 - 转义特殊字符 """ if not value: return "" # 去除首尾空格 value = value.strip() # 限制长度 if len(value) > max_length: value = value[:max_length] # 转义HTML特殊字符(防止XSS) html_chars = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/' } for char, escape in html_chars.items(): value = value.replace(char, escape) return value @staticmethod def validate_student_no(student_no: str) -> bool: """验证学号格式(数字+字母,长度4-20)""" if not student_no: return False if len(student_no) < 4 or len(student_no) > 20: return False # 字母数字组合 return student_no.isalnum() @staticmethod def validate_phone(phone: str) -> bool: """验证手机号格式(中国手机号)""" if not phone: return False pattern = r'^1[3-9]\d{9}$' return bool(re.match(pattern, phone)) @staticmethod def validate_points_change(points: int, max_abs: int = 100) -> tuple: """ 验证分值变动 返回: (是否有效, 错误信息) """ if points == 0: return False, "分值不能为0" if abs(points) > max_abs: return False, f"单次分值变动不能超过{max_abs}分" return True, "" # 单例导出 security = SecurityUtils()