修复后台弱密码提示误读本地配置问题

This commit is contained in:
liuwei
2026-05-06 08:46:11 +08:00
parent 8957799b76
commit 3730694465
2 changed files with 52 additions and 12 deletions

View File

@@ -54,18 +54,32 @@ def login():
return jsonify({"success": False, "error": error}), 429
return render_template('login.html', error=error)
# 优先使用数据库账号体系鉴权;若不可用则回退旧配置模式,保证兼容存量部署
# 优先使用数据库账号体系鉴权。
#
# 这里特意不再做“只要数据库校验失败就回退本地配置密码”的粗暴兜底,
# 原因有两点:
# 1. 一旦数据库里已经存在该管理员账号,真实口令状态应当以数据库为准;
# 2. 如果继续无条件回退本地 config.toml就会出现“数据库已改密但本地默认密码仍然生效”
# 的安全问题,也会让弱密码提示误判。
#
# 兼容策略调整为:
# - 数据库可用且账号存在:只认数据库;
# - 数据库不可用,或数据库里根本没有这个账号:才回退旧配置模式。
login_ok = False
db_account_exists = False
if admin_db:
try:
login_ok = admin_db.verify_admin_password(username, password)
db_account_exists = bool(admin_db.get_admin_by_username(username))
if db_account_exists:
login_ok = admin_db.verify_admin_password(username, password)
if login_ok:
admin_db.mark_login_success(username, request.remote_addr or "")
except Exception as e:
logger.error(f"数据库账号登录校验异常,回退配置模式: {e}")
login_ok = False
db_account_exists = False
if not login_ok:
if not login_ok and not db_account_exists:
login_ok = (username == server.username and password == server.password)
if login_ok:
@@ -107,11 +121,18 @@ def logout():
@login_required
def get_security_status():
"""返回当前登录管理员的安全状态。"""
server = current_app.dashboard_server
username = str(session.get("username", "") or "").strip()
# 安全状态这里改成“实时重算 + 回写 session”而不是只读登录瞬间写入的旧值
# 1. 避免管理员已经在数据库里改了密码,但当前会话仍保留旧的 force_password_change 标记;
# 2. 避免本地配置和数据库状态不一致时,前端一直弹出错误的弱密码提示。
force_password_change = bool(server.should_force_password_change(username)) if username else False
session["force_password_change"] = force_password_change
return jsonify({
"success": True,
"data": {
"force_password_change": bool(session.get("force_password_change", False)),
"session_timeout_minutes": int(current_app.dashboard_server.get_auth_policy().get("session_timeout_minutes", 480)),
"force_password_change": force_password_change,
"session_timeout_minutes": int(server.get_auth_policy().get("session_timeout_minutes", 480)),
}
})

View File

@@ -307,16 +307,35 @@ class DashboardServer:
def should_force_password_change(self, username: str) -> bool:
"""判断当前管理员是否应该被强制提示修改密码。"""
admin_db = getattr(self, "admin_account_db", None)
if admin_db and admin_db.is_using_risky_password(username):
return True
normalized_username = str(username or "").strip()
if not normalized_username:
return False
# 判断弱密码时优先且尽量只相信数据库中的真实账号状态。
#
# 之前的问题在于:
# 1. 代码先看数据库,再继续拿本地 config.toml 的默认用户名/密码做补充判断;
# 2. 如果数据库中的管理员已经改成强密码,但本地配置仍保留 admin/admin123
# 前端仍会被误判成“当前账号在使用弱密码”;
# 3. 这会让后台改密提示和真实账号状态脱节。
#
# 现在的策略改成:
# - 数据库里存在该管理员账号:只按数据库口令哈希判断;
# - 数据库里不存在该账号,才允许回退到旧配置模式,兼容尚未迁移完成的老部署。
if admin_db:
try:
admin_row = admin_db.get_admin_by_username(normalized_username)
if admin_row is not None:
return admin_db.is_using_risky_password(normalized_username)
except Exception as e:
# 安全提示属于辅助能力,数据库偶发异常时不应因为误判把用户“锁”在改密弹窗里。
# 因此这里记录 warning并继续走兼容兜底而不是直接强制提示弱密码。
self.LOG.warning(f"读取后台账号安全状态失败,将尝试兼容兜底判断: username={normalized_username}, error={e}")
# 数据库体系不可用时,再回退配置值判断,至少把默认 admin/admin123 识别出来。
fallback_username = str(self.username or "").strip()
fallback_password = str(self.password or "").strip()
return (
str(username or "").strip() == fallback_username
and fallback_password in getattr(admin_db, "RISKY_PASSWORDS", {"admin123", "admin"})
)
risky_passwords = getattr(admin_db, "RISKY_PASSWORDS", {"admin123", "admin"})
return normalized_username == fallback_username and fallback_password in risky_passwords
def _register_blueprints(self, app):
"""注册所有蓝图"""