Skip to content

Commit

Permalink
feat: 启用用户时清楚用户密码错误次数 (#638)
Browse files Browse the repository at this point in the history
  • Loading branch information
zgqgit committed Jun 14, 2024
2 parents 4a8d7eb + 3070f9f commit 9917de3
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 16 deletions.
40 changes: 27 additions & 13 deletions src/backend/bisheng/api/v1/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ async def sso(*, user: UserCreate):
raise ValueError('不支持接口')


def get_error_password_key(username: str):
return USER_PASSWORD_ERROR + username


@router.post('/user/login', response_model=UnifiedResponseModel[UserRead], status_code=201)
async def login(*, user: UserLogin, Authorize: AuthJWT = Depends()):
# 验证码校验
Expand All @@ -130,22 +134,22 @@ async def login(*, user: UserLogin, Authorize: AuthJWT = Depends()):
# 判断是否需要记录错误次数
if not password_conf.login_error_time_window or not password_conf.max_error_times:
return UserValidateError.return_resp()
error_key = USER_PASSWORD_ERROR + db_user.user_name
error_num = redis_client.get(error_key)
# 错误次数加1
error_key = get_error_password_key(user.user_name)
error_num = redis_client.incr(error_key)
if error_num == 1:
# 首次设置key的过期时间
redis_client.expire_key(error_key, password_conf.login_error_time_window * 60)
if error_num and int(error_num) >= password_conf.max_error_times:
# 错误次数到达上限,封禁账号
db_user.delete = 1
UserDao.update_user(db_user)
raise HTTPException(status_code=500, detail='该账号已被禁用,请联系管理员')

# 设置错误次数
redis_client.incr(error_key, password_conf.login_error_time_window * 60)
return UserValidateError.return_resp()

# 判断下密码是否长期未修改
if password_conf.password_valid_period and password_conf.password_valid_period > 0:
if (datetime.now() -
db_user.password_update_time).days >= password_conf.password_valid_period:
if (datetime.now() - db_user.password_update_time).days >= password_conf.password_valid_period:
return UserPasswordExpireError.return_resp()

access_token, refresh_token, role = gen_user_jwt(db_user)
Expand Down Expand Up @@ -298,12 +302,18 @@ def get_user_groups(user: User, group_cache: Dict) -> List[Dict]:


@router.post('/user/update', status_code=201)
async def update(*, user: UserUpdate, Authorize: AuthJWT = Depends()):
Authorize.jwt_required()
if 'admin' != json.loads(Authorize.get_jwt_subject()).get('role'):
raise HTTPException(status_code=500, detail='无查看权限')
with session_getter() as session:
db_user = session.get(User, user.user_id)
async def update(*, user: UserUpdate, login_user: UserPayload = Depends(get_login_user)):
db_user = UserDao.get_user(user.user_id)
if not db_user:
raise HTTPException(status_code=500, detail='用户不存在')

if not login_user.is_admin():
# 检查下是否是用户组的管理员
user_group = UserGroupDao.get_user_group(db_user.user_id)
user_group = [one.group_id for one in user_group]
if not login_user.check_groups_admin(user_group):
raise HTTPException(status_code=500, detail='无查看权限')

# check if user already exist
if db_user and user.delete is not None:
# 判断是否是管理员
Expand All @@ -314,6 +324,10 @@ async def update(*, user: UserUpdate, Authorize: AuthJWT = Depends()):
if admin:
raise HTTPException(status_code=500, detail='不能操作管理员')
db_user.delete = user.delete
if db_user.delete == 0: # 启用用户
# 清理密码错误次数的计数
error_key = get_error_password_key(db_user.user_name)
redis_client.delete(error_key)
with session_getter() as session:
session.add(db_user)
session.commit()
Expand Down
7 changes: 7 additions & 0 deletions src/backend/bisheng/cache/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ def incr(self, key, expiration=3600) -> int:
finally:
self.close()

def expire_key(self, key, expiration: int):
try:
self.cluster_nodes(key)
self.connection.expire(key, expiration)
finally:
self.close()

def delete(self, key):
try:
self.cluster_nodes(key)
Expand Down
12 changes: 12 additions & 0 deletions src/backend/bisheng/initdb_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ gpts:
tianyancha:
api_key: ""

password_valid_period: Optional[int] = Field(description='密码超过X天必须进行修改, 登录提示重新修改密码')
login_error_time_window: Optional[int] = Field(description='登录错误时间窗口,单位分钟')
max_error_times: Optional[int] = Field(description='最大错误次数,超过后会封禁用户')

# 密码配置
password_conf:
# 密码超过X天必须进行修改, 登录提示重新修改密码。大于0策略才生效
password_valid_period: 2
# 登录错误时间窗口,单位分钟。在错误时间窗口内超过最大错误次数会封禁用户。两个值都大于0才生效
login_error_time_window: 10
# 最大错误次数,超过后会封禁用户
max_error_times: 5

system_login_method:
# SSO 登录
Expand Down
6 changes: 3 additions & 3 deletions src/backend/bisheng/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def set_handlers(cls, value):


class PasswordConf(BaseModel):
password_valid_period: Optional[int] = Field(description='密码超过X天必须进行修改, 登录提示重新修改密码')
login_error_time_window: Optional[int] = Field(description='登录错误时间窗口,单位分钟')
max_error_times: Optional[int] = Field(description='最大错误次数,超过后会封禁用户')
password_valid_period: Optional[int] = Field(default=0, description='密码超过X天必须进行修改, 登录提示重新修改密码')
login_error_time_window: Optional[int] = Field(default=0, description='登录错误时间窗口,单位分钟')
max_error_times: Optional[int] = Field(default=0, description='最大错误次数,超过后会封禁用户')


class Settings(BaseSettings):
Expand Down

0 comments on commit 9917de3

Please sign in to comment.