v0.1测试
This commit is contained in:
131
backend/utils/security.py
Normal file
131
backend/utils/security.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# ===========================================
|
||||
# 班级操行分管理系统 - 后端服务
|
||||
#
|
||||
# 开发者: 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:
|
||||
"""
|
||||
验证密码强度
|
||||
返回: (是否有效, 错误信息)
|
||||
"""
|
||||
if len(password) < 6:
|
||||
return False, "密码长度至少6位"
|
||||
if len(password) > 20:
|
||||
return False, "密码长度不能超过20位"
|
||||
|
||||
# 检查是否包含至少一个数字
|
||||
if not any(c.isdigit() for c in password):
|
||||
return False, "密码必须包含至少一个数字"
|
||||
|
||||
# 检查是否包含至少一个字母
|
||||
if not any(c.isalpha() for c in password):
|
||||
return False, "密码必须包含至少一个字母"
|
||||
|
||||
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 f"单次分值变动不能超过{max_abs}分"
|
||||
return True, ""
|
||||
|
||||
|
||||
# 单例导出
|
||||
security = SecurityUtils()
|
||||
Reference in New Issue
Block a user