240 lines
8.8 KiB
Python
240 lines
8.8 KiB
Python
|
|
'''用户认证登录模块'''
|
|||
|
|
import json
|
|||
|
|
import os
|
|||
|
|
import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
from typing import Tuple, Optional
|
|||
|
|
|
|||
|
|
class SimpleAuth:
|
|||
|
|
"""用户认证管理器"""
|
|||
|
|
|
|||
|
|
def __init__(self, config_path: str = None):
|
|||
|
|
"""
|
|||
|
|
初始化认证管理器
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
config_path: 用户配置文件路径
|
|||
|
|
"""
|
|||
|
|
self.config_path = config_path
|
|||
|
|
self.users = {}
|
|||
|
|
|
|||
|
|
# 初始化时加载用户数据
|
|||
|
|
if config_path:
|
|||
|
|
self.load_users()
|
|||
|
|
|
|||
|
|
def load_users(self) -> bool:
|
|||
|
|
"""加载用户数据"""
|
|||
|
|
try:
|
|||
|
|
if not self.config_path:
|
|||
|
|
print("未设置配置文件路径")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 如果文件不存在,创建空文件
|
|||
|
|
if not os.path.exists(self.config_path):
|
|||
|
|
print(f"配置文件不存在,创建空文件: {self.config_path}")
|
|||
|
|
return self.create_empty_file()
|
|||
|
|
|
|||
|
|
# 读取文件内容
|
|||
|
|
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|||
|
|
content = f.read().strip()
|
|||
|
|
|
|||
|
|
if not content: # 空文件
|
|||
|
|
self.users = {}
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
# 解析JSON
|
|||
|
|
self.users = json.loads(content)
|
|||
|
|
|
|||
|
|
print(f"已加载 {len(self.users)} 个用户")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except json.JSONDecodeError:
|
|||
|
|
print("配置文件格式错误,重置为空文件")
|
|||
|
|
return self.create_empty_file()
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"加载用户数据失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def create_empty_file(self) -> bool:
|
|||
|
|
"""创建空的配置文件"""
|
|||
|
|
try:
|
|||
|
|
if not self.config_path:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
|
|||
|
|
self.users = {}
|
|||
|
|
|
|||
|
|
with open(self.config_path, 'w', encoding='utf-8') as f:
|
|||
|
|
json.dump({}, f, indent=2, ensure_ascii=False)
|
|||
|
|
|
|||
|
|
print(f"已创建空白配置文件: {self.config_path}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"创建配置文件失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def save_users(self) -> bool:
|
|||
|
|
"""保存用户数据"""
|
|||
|
|
try:
|
|||
|
|
if not self.config_path:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
|
|||
|
|
|
|||
|
|
with open(self.config_path, 'w', encoding='utf-8') as f:
|
|||
|
|
json.dump(self.users, f, indent=2, ensure_ascii=False)
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"保存用户数据失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def authenticate(self, username: str, password: str) -> Tuple[bool, str]:
|
|||
|
|
"""
|
|||
|
|
验证用户凭据
|
|||
|
|
|
|||
|
|
重要:如果用户不存在,则创建用户(首次登录即注册)
|
|||
|
|
"""
|
|||
|
|
if not username or not password:
|
|||
|
|
return False, "用户名和密码不能为空"
|
|||
|
|
|
|||
|
|
# 加载用户数据
|
|||
|
|
if not self.load_users():
|
|||
|
|
return False, "加载用户数据失败"
|
|||
|
|
|
|||
|
|
# 检查用户名是否存在(作为字典键)
|
|||
|
|
if username not in self.users:
|
|||
|
|
# 首次登录,自动创建用户
|
|||
|
|
print(f"用户 '{username}' 不存在,首次登录将自动创建用户")
|
|||
|
|
return self.create_first_user(username, password)
|
|||
|
|
|
|||
|
|
# 检查密码是否正确
|
|||
|
|
user_info = self.users[username]
|
|||
|
|
if user_info.get("password") != password:
|
|||
|
|
return False, "密码错误"
|
|||
|
|
|
|||
|
|
# 更新最后登录时间
|
|||
|
|
user_info["last_login"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
self.save_users()
|
|||
|
|
|
|||
|
|
return True, "登录成功"
|
|||
|
|
|
|||
|
|
def create_first_user(self, username: str, password: str) -> Tuple[bool, str]:
|
|||
|
|
"""创建第一个用户(首次登录)"""
|
|||
|
|
try:
|
|||
|
|
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
|
|||
|
|
# 创建用户信息
|
|||
|
|
self.users[username] = {
|
|||
|
|
"username": username,
|
|||
|
|
"password": password,
|
|||
|
|
"secondary_password": "", # 二级密码初始为空,首次登录后设置
|
|||
|
|
"created_at": current_time,
|
|||
|
|
"last_login": current_time,
|
|||
|
|
"is_first_user": True
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 保存用户数据
|
|||
|
|
if self.save_users():
|
|||
|
|
print(f"已创建用户 '{username}' 并登录")
|
|||
|
|
return True, f"首次登录成功,已创建用户 '{username}'"
|
|||
|
|
else:
|
|||
|
|
return False, "创建用户失败"
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"创建用户失败: {e}")
|
|||
|
|
return False, f"创建用户失败: {str(e)}"
|
|||
|
|
|
|||
|
|
def set_secondary_password(self, username: str, secondary_password: str) -> Tuple[bool, str]:
|
|||
|
|
"""设置二级密码"""
|
|||
|
|
try:
|
|||
|
|
if not self.load_users():
|
|||
|
|
return False, "加载用户数据失败"
|
|||
|
|
|
|||
|
|
if username not in self.users:
|
|||
|
|
return False, "用户不存在"
|
|||
|
|
|
|||
|
|
if not secondary_password:
|
|||
|
|
return False, "二级密码不能为空"
|
|||
|
|
|
|||
|
|
# 更新二级密码
|
|||
|
|
self.users[username]["secondary_password"] = secondary_password
|
|||
|
|
self.users[username]["secondary_password_updated_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|||
|
|
|
|||
|
|
if self.save_users():
|
|||
|
|
return True, "二级密码设置成功"
|
|||
|
|
else:
|
|||
|
|
return False, "保存二级密码失败"
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
return False, f"设置二级密码失败: {str(e)}"
|
|||
|
|
|
|||
|
|
def verify_secondary_password(self, username: str, secondary_password: str) -> Tuple[bool, str]:
|
|||
|
|
"""验证二级密码"""
|
|||
|
|
try:
|
|||
|
|
if not self.load_users():
|
|||
|
|
return False, "加载用户数据失败"
|
|||
|
|
|
|||
|
|
if username not in self.users:
|
|||
|
|
return False, "用户不存在"
|
|||
|
|
|
|||
|
|
user_info = self.users[username]
|
|||
|
|
|
|||
|
|
# 检查用户是否设置了二级密码
|
|||
|
|
if not user_info.get("secondary_password"):
|
|||
|
|
return False, "未设置二级密码,请先设置"
|
|||
|
|
|
|||
|
|
# 验证二级密码
|
|||
|
|
if user_info.get("secondary_password") != secondary_password:
|
|||
|
|
return False, "二级密码错误"
|
|||
|
|
|
|||
|
|
return True, "二级密码验证成功"
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
return False, f"验证二级密码失败: {str(e)}"
|
|||
|
|
|
|||
|
|
def get_secondary_password_status(self, username: str) -> Tuple[bool, str]:
|
|||
|
|
"""获取二级密码设置状态"""
|
|||
|
|
try:
|
|||
|
|
if not self.load_users():
|
|||
|
|
return False, "加载用户数据失败"
|
|||
|
|
|
|||
|
|
if username not in self.users:
|
|||
|
|
return False, "用户不存在"
|
|||
|
|
|
|||
|
|
user_info = self.users[username]
|
|||
|
|
|
|||
|
|
if user_info.get("secondary_password"):
|
|||
|
|
return True, f"已设置二级密码(更新于: {user_info.get('secondary_password_updated_at', '未知时间')})"
|
|||
|
|
else:
|
|||
|
|
return False, "未设置二级密码"
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
return False, f"获取二级密码状态失败: {str(e)}"
|
|||
|
|
|
|||
|
|
def get_default_config_path(self) -> str:
|
|||
|
|
"""获取默认配置文件路径"""
|
|||
|
|
return str(Path.home() / ".config_editor" / "users.json")
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def validate_config_path(config_path: str) -> bool:
|
|||
|
|
"""验证配置文件路径是否有效"""
|
|||
|
|
try:
|
|||
|
|
# 检查路径是否包含有效的目录
|
|||
|
|
dir_path = os.path.dirname(config_path)
|
|||
|
|
if not dir_path:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查是否是绝对路径
|
|||
|
|
if not os.path.isabs(config_path):
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查文件扩展名(可选)
|
|||
|
|
if not config_path.endswith(('.json', '.txt', '.dat')):
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
except Exception:
|
|||
|
|
return False
|