Skip to content

Commit 54bd6ac

Browse files
committed
feat:新增登录密码错误次数限制及账号解锁功能
fix:修复已知bug
1 parent e881842 commit 54bd6ac

File tree

8 files changed

+82
-4
lines changed

8 files changed

+82
-4
lines changed

dash-fastapi-backend/module_admin/controller/log_controller.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,22 @@ async def clear_system_login_log(request: Request, clear_login_log: ClearLoginLo
134134
return response_500(data="", message=str(e))
135135

136136

137+
@logController.post("/login/unlock", response_model=CrudLogResponse, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:remove'))])
138+
@log_decorator(title='登录日志管理', business_type=9)
139+
async def clear_system_login_log(request: Request, unlock_user: UnlockUser, query_db: Session = Depends(get_db)):
140+
try:
141+
unlock_user_result = await LoginLogService.unlock_user_services(request, unlock_user)
142+
if unlock_user_result.is_success:
143+
logger.info(unlock_user_result.message)
144+
return response_200(data=unlock_user_result, message=unlock_user_result.message)
145+
else:
146+
logger.warning(unlock_user_result.message)
147+
return response_400(data="", message=unlock_user_result.message)
148+
except Exception as e:
149+
logger.exception(e)
150+
return response_500(data="", message=str(e))
151+
152+
137153
@logController.post("/login/export", dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:export'))])
138154
@log_decorator(title='登录日志管理', business_type=5)
139155
async def export_system_login_log_list(request: Request, login_log_query: LoginLogQueryModel, query_db: Session = Depends(get_db)):

dash-fastapi-backend/module_admin/controller/login_controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
async def login(request: Request, user: UserLogin, query_db: Session = Depends(get_db)):
2020
try:
2121
result = await authenticate_user(request, query_db, user)
22-
if result in ['用户不存在', '密码错误', '用户已停用', '验证码已失效', '验证码错误']:
22+
if result in ['用户不存在', '密码错误', '用户已停用', '验证码已失效', '验证码错误', '账号已锁定,请稍后再试', '10分钟内密码已输错超过5次,账号已锁定,请10分钟后再试']:
2323
logger.warning(result)
2424
return response_400(data="", message=result)
2525

dash-fastapi-backend/module_admin/entity/vo/log_vo.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ class ClearLoginLogModel(BaseModel):
128128
oper_type: str
129129

130130

131+
class UnlockUser(BaseModel):
132+
"""
133+
解锁用户模型
134+
"""
135+
user_name: str
136+
137+
131138
class CrudLogResponse(BaseModel):
132139
"""
133140
操作各类日志响应模型

dash-fastapi-backend/module_admin/service/log_service.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ def clear_login_log_services(cls, result_db: Session, page_object: ClearLoginLog
221221

222222
return CrudLogResponse(**result)
223223

224+
@classmethod
225+
async def unlock_user_services(cls, request: Request, unlock_user: UnlockUser):
226+
locked_user = await request.app.state.redis.get(f"account_lock:{unlock_user.user_name}")
227+
if locked_user:
228+
await request.app.state.redis.delete(f"account_lock:{unlock_user.user_name}")
229+
result = dict(is_success=True, message='解锁成功')
230+
else:
231+
result = dict(is_success=False, message='该用户未锁定')
232+
return CrudLogResponse(**result)
233+
224234
@staticmethod
225235
def export_login_log_list_services(login_log_list: List):
226236
"""

dash-fastapi-backend/module_admin/service/login_service.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,32 @@ async def authenticate_user(request: Request, query_db: Session, login_user: Use
107107
:param login_user: 登录用户对象
108108
:return: 校验结果
109109
"""
110-
user = login_by_account(query_db, login_user.user_name)
110+
account_lock = await request.app.state.redis.get(f"account_lock:{login_user.user_name}")
111+
if login_user.user_name == account_lock:
112+
return '账号已锁定,请稍后再试'
111113
captcha_value = await request.app.state.redis.get(f'captcha_codes:{login_user.session_id}')
112114
if not captcha_value:
113115
return '验证码已失效'
114116
if login_user.captcha != str(captcha_value):
115117
return '验证码错误'
118+
user = login_by_account(query_db, login_user.user_name)
116119
if not user[0]:
117120
return '用户不存在'
118121
if not verify_password(login_user.password, user[0].password):
122+
cache_password_error_count = await request.app.state.redis.get(f"password_error_count:{login_user.user_name}")
123+
password_error_counted = 0
124+
if cache_password_error_count:
125+
password_error_counted = cache_password_error_count
126+
password_error_count = int(password_error_counted) + 1
127+
await request.app.state.redis.set(f"password_error_count:{login_user.user_name}", password_error_count, ex=timedelta(minutes=10))
128+
if password_error_count > 5:
129+
await request.app.state.redis.delete(f"password_error_count:{login_user.user_name}")
130+
await request.app.state.redis.set(f"account_lock:{login_user.user_name}", login_user.user_name, ex=timedelta(minutes=10))
131+
return '10分钟内密码已输错超过5次,账号已锁定,请10分钟后再试'
119132
return '密码错误'
120133
if user[0].status == '1':
121134
return '用户已停用'
135+
await request.app.state.redis.delete(f"password_error_count:{login_user.user_name}")
122136
return user
123137

124138

dash-fastapi-frontend/api/log.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ def clear_login_log_api(page_obj: dict):
4141
return api_request(method='post', url='/system/log/login/clear', is_headers=True, json=page_obj)
4242

4343

44+
def unlock_user_api(page_obj: dict):
45+
46+
return api_request(method='post', url='/system/log/login/unlock', is_headers=True, json=page_obj)
47+
48+
4449
def export_login_log_list_api(page_obj: dict):
4550

4651
return api_request(method='post', url='/system/log/login/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/callbacks/monitor_c/logininfor_c.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import feffery_utils_components as fuc
88

99
from server import app
10-
from api.log import get_login_log_list_api, delete_login_log_api, clear_login_log_api, export_login_log_list_api
10+
from api.log import get_login_log_list_api, delete_login_log_api, clear_login_log_api, unlock_user_api, export_login_log_list_api
1111

1212

1313
@app.callback(
@@ -246,3 +246,29 @@ def reset_login_log_export_status(data):
246246
return None
247247

248248
return dash.no_update
249+
250+
251+
@app.callback(
252+
[Output('api-check-token', 'data', allow_duplicate=True),
253+
Output('global-message-container', 'children', allow_duplicate=True)],
254+
Input('login_log-unlock', 'nClicks'),
255+
State('login_log-list-table', 'selectedRows'),
256+
prevent_initial_call=True
257+
)
258+
def unlock_user(unlock_click, selected_rows):
259+
if unlock_click:
260+
user_name = selected_rows[0].get('user_name')
261+
unlock_info_res = unlock_user_api(dict(user_name=user_name))
262+
if unlock_info_res.get('code') == 200:
263+
264+
return [
265+
{'timestamp': time.time()},
266+
fuc.FefferyFancyMessage('解锁成功', type='success')
267+
]
268+
269+
return [
270+
{'timestamp': time.time()},
271+
fuc.FefferyFancyMessage('解锁失败', type='error')
272+
]
273+
274+
return [dash.no_update] * 2

dash-fastapi-frontend/config/global_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class RouterConfig:
1515
]
1616

1717
# 静态路由列表
18-
STATIC_VALID_PATHNAME = ['/', '/user/profile']
18+
STATIC_VALID_PATHNAME = ['/', '/login', '/forget', '/user/profile']
1919

2020

2121
class ApiBaseUrlConfig:

0 commit comments

Comments
 (0)