@@ -77,58 +77,134 @@ def get_auth_setting():
7777
7878 @staticmethod
7979 def login (instance ):
80+ # 解密数据
8081 username = instance .get ("username" , "" )
81- encryptedData = instance .get ("encryptedData" , "" )
82- if encryptedData :
83- json_data = json .loads (decrypt (encryptedData ))
84- instance .update (json_data )
82+ encrypted_data = instance .get ("encryptedData" , "" )
83+
84+ if encrypted_data :
85+ decrypted_data = json .loads (decrypt (encrypted_data ))
86+ instance .update (decrypted_data )
8587 try :
8688 LoginRequest (data = instance ).is_valid (raise_exception = True )
89+ except serializers .ValidationError :
90+ raise
8791 except Exception as e :
88- record_login_fail (username )
89- raise e
90- auth_setting = LoginSerializer .get_auth_setting ()
92+ raise AppApiException (500 , str (e ))
9193
92- max_attempts = auth_setting .get ("max_attempts" , 1 )
9394 password = instance .get ("password" )
9495 captcha = instance .get ("captcha" , "" )
9596
96- # 判断是否需要验证码
97- need_captcha = False
98- if max_attempts == - 1 :
99- need_captcha = False
100- elif max_attempts > 0 :
101- fail_count = cache .get (system_get_key (f'system_{ username } ' ), version = system_version ) or 0
102- need_captcha = fail_count >= max_attempts
103-
104- if need_captcha :
105- if not captcha :
106- raise AppApiException (1005 , _ ("Captcha is required" ))
107-
108- captcha_cache = cache .get (
109- Cache_Version .CAPTCHA .get_key (captcha = f"system_{ username } " ),
110- version = Cache_Version .CAPTCHA .get_version ()
111- )
112- if captcha_cache is None or captcha .lower () != captcha_cache :
113- raise AppApiException (1005 , _ ("Captcha code error or expiration" ))
97+ # 获取认证配置
98+ auth_setting = LoginSerializer .get_auth_setting ()
99+ max_attempts = auth_setting .get ("max_attempts" , 1 )
100+ failed_attempts = auth_setting .get ("failed_attempts" , 5 )
101+ lock_time = auth_setting .get ("lock_time" , 10 )
102+
103+ # 检查许可证有效性
104+ license_validator = DatabaseModelManage .get_model ('license_is_valid' ) or (lambda : False )
105+ is_license_valid = license_validator () if license_validator () is not None else False
106+
107+ if is_license_valid :
108+ # 检查账户是否被锁定
109+ if LoginSerializer ._is_account_locked (username ):
110+ raise AppApiException (
111+ 1005 ,
112+ _ ("This account has been locked for %s minutes, please try again later" ) % lock_time
113+ )
114+
115+ # 验证验证码
116+ if LoginSerializer ._need_captcha (username , max_attempts ):
117+ LoginSerializer ._validate_captcha (username , captcha )
118+
119+ # 验证用户凭据
120+ user = QuerySet (User ).filter (
121+ username = username ,
122+ password = password_encrypt (password )
123+ ).first ()
114124
115- user = QuerySet (User ).filter (username = username , password = password_encrypt (password )).first ()
116- if user is None :
117- record_login_fail (username )
125+ if not user :
126+ LoginSerializer ._handle_failed_login (username , is_license_valid , failed_attempts , lock_time )
118127 raise AppApiException (500 , _ ('The username or password is incorrect' ))
128+
119129 if not user .is_active :
120- record_login_fail (username )
121130 raise AppApiException (1005 , _ ("The user has been disabled, please contact the administrator!" ))
131+
132+ # 清除失败计数并生成令牌
122133 cache .delete (system_get_key (f'system_{ username } ' ), version = system_version )
123- token = signing .dumps ({'username' : user .username ,
124- 'id' : str (user .id ),
125- 'email' : user .email ,
126- 'type' : AuthenticationType .SYSTEM_USER .value })
134+ cache .delete (system_get_key (f'system_{ username } _lock' ), version = system_version )
135+ token = signing .dumps ({
136+ 'username' : user .username ,
137+ 'id' : str (user .id ),
138+ 'email' : user .email ,
139+ 'type' : AuthenticationType .SYSTEM_USER .value
140+ })
141+
127142 version , get_key = Cache_Version .TOKEN .value
128143 timeout = CONFIG .get_session_timeout ()
129144 cache .set (get_key (token ), user , timeout = timeout , version = version )
145+
130146 return {'token' : token }
131147
148+ @staticmethod
149+ def _is_account_locked (username : str ) -> bool :
150+ """检查账户是否被锁定"""
151+ lock_cache = cache .get (system_get_key (f'system_{ username } _lock' ), version = system_version )
152+ return bool (lock_cache )
153+
154+ @staticmethod
155+ def _need_captcha (username : str , max_attempts : int ) -> bool :
156+ """判断是否需要验证码"""
157+ if max_attempts == - 1 :
158+ return False
159+ elif max_attempts > 0 :
160+ fail_count = cache .get (system_get_key (f'system_{ username } ' ), version = system_version ) or 0
161+ return fail_count >= max_attempts
162+ return True
163+
164+ @staticmethod
165+ def _validate_captcha (username : str , captcha : str ) -> None :
166+ """验证验证码"""
167+ if not captcha :
168+ raise AppApiException (1005 , _ ("Captcha is required" ))
169+
170+ captcha_cache = cache .get (
171+ Cache_Version .CAPTCHA .get_key (captcha = f"system_{ username } " ),
172+ version = Cache_Version .CAPTCHA .get_version ()
173+ )
174+
175+ if captcha_cache is None or captcha .lower () != captcha_cache :
176+ raise AppApiException (1005 , _ ("Captcha code error or expiration" ))
177+
178+ @staticmethod
179+ def _handle_failed_login (username : str , is_license_valid : bool , failed_attempts : int , lock_time : int ) -> None :
180+ """处理登录失败"""
181+ record_login_fail (username )
182+
183+ if not is_license_valid or failed_attempts <= 0 :
184+ return
185+
186+ fail_count = cache .get (system_get_key (f'system_{ username } ' ), version = system_version ) or 0
187+ remain_attempts = failed_attempts - fail_count
188+
189+ if remain_attempts > 0 :
190+ raise AppApiException (
191+ 1005 ,
192+ _ ("Login failed %s times, account will be locked, you have %s more chances !" ) % (
193+ failed_attempts , remain_attempts
194+ )
195+ )
196+ elif remain_attempts == 0 :
197+ cache .set (
198+ system_get_key (f'system_{ username } _lock' ),
199+ 1 ,
200+ timeout = lock_time * 60 ,
201+ version = system_version
202+ )
203+ raise AppApiException (
204+ 1005 ,
205+ _ ("This account has been locked for %s minutes, please try again later" ) % lock_time
206+ )
207+
132208
133209class CaptchaResponse (serializers .Serializer ):
134210 """
@@ -171,7 +247,6 @@ def chat_generate(username: str, type: str = 'chat', access_token: str = ''):
171247 fail_count = cache .get (system_get_key (f'{ type } _{ username } ' ), version = system_version ) or 0
172248 need_captcha = fail_count >= max_attempts
173249
174-
175250 return CaptchaSerializer ._generate_captcha_if_needed (username , type , need_captcha )
176251
177252 @staticmethod
0 commit comments