This commit is contained in:
ping 2026-04-28 17:47:26 +08:00
parent f148392bbe
commit f853f6fce0

View File

@ -1,3 +1,98 @@
async def handle_login_failed(user_name):
from datetime import datetime, timedelta
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
current_time = datetime.now()
# 1. 查询该用户当前的失败记录
record = await sor.sqlExe("SELECT first_failed_time, failed_count FROM login_fail_log WHERE user_name = '%s'" % user_name, {})
if not record:
# === 第一次失败 ===
await sor.sqlExe("""
INSERT INTO login_fail_log (user_name, first_failed_time, failed_count)
VALUES ('%s', '%s', 1)
""" % (user_name, current_time), {})
return # 正常失败,不需要等待
first_failed_time = record[0]['first_failed_time']
failed_count = record[0]['failed_count']
first_failed_time = datetime.strptime(first_failed_time, '%Y-%m-%d %H:%M:%S')
# 2. 判断是否还在10分钟窗口内
time_diff = (current_time - first_failed_time).total_seconds() / 60 # 分钟
if time_diff <= 10:
# 还在10分钟窗口内失败次数+1
new_count = failed_count + 1
if new_count >= 3:
# === 第三次及以上失败:触发锁定 ===
# 更新失败次数,并记录锁定开始时间
await sor.sqlExe("""
UPDATE login_fail_log
SET failed_count = 3, last_failed_time = '%s', lock_until = '%s'
WHERE user_name = '%s'
""" % (current_time, current_time + timedelta(minutes=20), user_name), {})
print("十分钟内登录失败三次请等待20分钟后再试")
else:
# 第二次失败
await sor.sqlExe("""
UPDATE login_fail_log
SET failed_count = '%s', last_failed_time = '%s'
WHERE user_name = '%s'
""" % (new_count, current_time, user_name), {})
# 可选:返回还剩几次机会
if new_count == 2:
print("登录失败还剩1次机会10分钟内")
else:
# === 已经超过10分钟窗口重置计数器 ===
await sor.sqlExe("""
UPDATE login_fail_log
SET first_failed_time = '%s', failed_count = 1, last_failed_time = '%s', lock_until = NULL
WHERE user_name = '%s'
""" % (current_time, current_time, user_name), {})
except Exception as e:
print("exception:", user_name, e)
return {'status': False, 'msg': '登录操作失败', 'error': str(e)}
async def check_login_allowed(user_name):
from datetime import datetime, timedelta
db = DBPools()
async with db.sqlorContext('kboss') as sor:
try:
record = await sor.sqlExe("SELECT lock_until, first_failed_time, failed_count FROM login_fail_log WHERE user_name = '%s'" % user_name, {})
current_time = datetime.now()
if not record:
return {'status': True} # 允许登录
record = record[0]
if record.get('lock_until'):
record['lock_until'] = datetime.strptime(record['lock_until'], '%Y-%m-%d %H:%M:%S')
# 检查是否处于锁定状态
if record.get('lock_until') and record['lock_until'] > current_time:
wait_minutes = (record['lock_until'] - current_time).total_seconds() // 60
msg = f"十分钟内登录失败三次,请等待{int(wait_minutes)}分钟后再试"
return {
'status': False,
'msg': msg
}
# 如果锁定时间已过,但还在检查窗口,也可以重置(可选)
return {'status': True} # 允许登录
except Exception as e:
print("exception:", user_name, e)
return {'status': False, 'msg': '登录操作失败, %s' % str(e)}
async def logintype(ns): async def logintype(ns):
""" """
1、判断用户是否为主级如果在reseller没要找到数据证明就是主级 1、判断用户是否为主级如果在reseller没要找到数据证明就是主级
@ -46,6 +141,12 @@ async def logintype(ns):
domain_name = ns.get('domain_name') domain_name = ns.get('domain_name')
if domain_name in ['www.opencomputing.cn', 'dev.opencomputing.cn', 'localhost:9527'] and ns.get('username') not in ['开元云(北京)科技有限公司', 'admin', 'kyy_root']: if domain_name in ['www.opencomputing.cn', 'dev.opencomputing.cn', 'localhost:9527'] and ns.get('username') not in ['开元云(北京)科技有限公司', 'admin', 'kyy_root']:
# 登录失败次数限制
login_allowed = await check_login_allowed(ns.get('username'))
if not login_allowed.get('status'):
return {'status': False, 'msg': login_allowed.get('msg')}
if not ns.get('mobile'): if not ns.get('mobile'):
return { return {
'status': False, 'status': False,
@ -77,11 +178,13 @@ async def logintype(ns):
} }
code = await sor.R('validatecode', {'id': ns.get('codeid'), 'vcode': ns.get('vcode')}) code = await sor.R('validatecode', {'id': ns.get('codeid'), 'vcode': ns.get('vcode')})
if len(code) < 1: if len(code) < 1:
await handle_login_failed(ns.get('username'))
return {'status': False, 'msg': '验证码不正确'} return {'status': False, 'msg': '验证码不正确'}
password = password_encode(ns['password']) password = password_encode(ns['password'])
users = await sor.R('users', {'username': ns.get('username'), 'password': password}) users = await sor.R('users', {'username': ns.get('username'), 'password': password})
if len(users) < 1: if len(users) < 1:
await handle_login_failed(ns.get('username'))
return {"status": False,'msg':'用户名或密码错误'} return {"status": False,'msg':'用户名或密码错误'}
return {'status': True} return {'status': True}
@ -100,6 +203,7 @@ async def logintype(ns):
password = password_encode(ns['password']) password = password_encode(ns['password'])
users = await sor.R('users', {'username': ns.get('username'), 'password': password}) users = await sor.R('users', {'username': ns.get('username'), 'password': password})
if len(users) < 1: if len(users) < 1:
await handle_login_failed(ns.get('username'))
return {"status": False,'msg':'用户名或密码错误'} return {"status": False,'msg':'用户名或密码错误'}
elif ns.get('username') == "admin": elif ns.get('username') == "admin":
return {'status': True} return {'status': True}