88"""
99import base64
1010import datetime
11+ import json
1112
1213from captcha .image import ImageCaptcha
1314from django .core import signing
1819
1920from common .constants .authentication_type import AuthenticationType
2021from common .constants .cache_version import Cache_Version
22+ from common .database_model_manage .database_model_manage import DatabaseModelManage
2123from common .exception .app_exception import AppApiException
2224from common .utils .common import password_encrypt , get_random_chars
2325from maxkb .const import CONFIG
2729class LoginRequest (serializers .Serializer ):
2830 username = serializers .CharField (required = True , max_length = 64 , help_text = _ ("Username" ), label = _ ("Username" ))
2931 password = serializers .CharField (required = True , max_length = 128 , label = _ ("Password" ))
30- captcha = serializers .CharField (required = True , max_length = 64 , label = _ ('captcha' ))
32+ captcha = serializers .CharField (required = False , max_length = 64 , label = _ ('captcha' ), allow_null = True ,
33+ allow_blank = True )
34+
35+
36+ system_version , system_get_key = Cache_Version .SYSTEM .value
3137
3238
3339class LoginResponse (serializers .Serializer ):
@@ -37,23 +43,70 @@ class LoginResponse(serializers.Serializer):
3743 token = serializers .CharField (required = True , label = _ ("token" ))
3844
3945
46+ def record_login_fail (username : str , expire : int = 3600 ):
47+ """记录登录失败次数"""
48+ if not username :
49+ return
50+ fail_key = system_get_key (f'system_{ username } ' )
51+ fail_count = cache .get (fail_key , version = system_version )
52+ if fail_count is None :
53+ cache .set (fail_key , 1 , timeout = expire , version = system_version )
54+ else :
55+ cache .incr (fail_key , 1 , version = system_version )
56+
57+
4058class LoginSerializer (serializers .Serializer ):
4159
4260 @staticmethod
4361 def login (instance ):
44- LoginRequest (data = instance ).is_valid (raise_exception = True )
45- username = instance .get ('username' )
46- password = instance .get ('password' )
47- captcha = instance .get ('captcha' )
48- captcha_cache = cache .get (Cache_Version .CAPTCHA .get_key (captcha = captcha .lower ()),
49- version = Cache_Version .CAPTCHA .get_version ())
50- if captcha_cache is None :
51- raise AppApiException (1005 , _ ("Captcha code error or expiration" ))
62+ username = instance .get ("username" , "" )
63+ try :
64+ LoginRequest (data = instance ).is_valid (raise_exception = True )
65+ except Exception as e :
66+ record_login_fail (username )
67+ raise e
68+ auth_setting_model = DatabaseModelManage .get_model ('auth_setting' )
69+ # 默认配置
70+ auth_setting = {}
71+ if auth_setting_model :
72+ setting_obj = auth_setting_model .objects .filter (param_key = 'auth_setting' ).first ()
73+ if setting_obj :
74+ try :
75+ auth_setting = json .loads (setting_obj .param_value ) or {}
76+ except Exception :
77+ auth_setting = {}
78+
79+ max_attempts = auth_setting .get ("max_attempts" , 0 )
80+ password = instance .get ("password" )
81+ captcha = instance .get ("captcha" , "" )
82+
83+ # 判断是否需要验证码
84+ need_captcha = True
85+ if max_attempts == - 1 :
86+ need_captcha = False
87+ elif max_attempts > 0 :
88+ fail_count = cache .get (system_get_key (f'system_{ username } ' ), version = system_version ) or 0
89+ need_captcha = fail_count >= max_attempts
90+
91+ if need_captcha :
92+ if not captcha :
93+ raise AppApiException (1005 , _ ("Captcha is required" ))
94+
95+ captcha_cache = cache .get (
96+ Cache_Version .CAPTCHA .get_key (captcha = f"system_{ username } " ),
97+ version = Cache_Version .CAPTCHA .get_version ()
98+ )
99+ if captcha_cache is None or captcha .lower () != captcha_cache :
100+ raise AppApiException (1005 , _ ("Captcha code error or expiration" ))
101+
52102 user = QuerySet (User ).filter (username = username , password = password_encrypt (password )).first ()
53103 if user is None :
104+ record_login_fail (username )
54105 raise AppApiException (500 , _ ('The username or password is incorrect' ))
55106 if not user .is_active :
107+ record_login_fail (username )
56108 raise AppApiException (1005 , _ ("The user has been disabled, please contact the administrator!" ))
109+ cache .delete (system_get_key (f'system_{ username } ' ), version = system_version )
57110 token = signing .dumps ({'username' : user .username ,
58111 'id' : str (user .id ),
59112 'email' : user .email ,
@@ -73,11 +126,11 @@ class CaptchaResponse(serializers.Serializer):
73126
74127class CaptchaSerializer (serializers .Serializer ):
75128 @staticmethod
76- def generate ():
129+ def generate (username : str , type : str = 'system' ):
77130 chars = get_random_chars ()
78131 image = ImageCaptcha ()
79132 data = image .generate (chars )
80133 captcha = base64 .b64encode (data .getbuffer ())
81- cache .set (Cache_Version .CAPTCHA .get_key (captcha = chars .lower ()), chars ,
82- timeout = 60 , version = Cache_Version .CAPTCHA .get_version ())
134+ cache .set (Cache_Version .CAPTCHA .get_key (captcha = f' { type } _ { username } ' ), chars .lower (),
135+ timeout = 300 , version = Cache_Version .CAPTCHA .get_version ())
83136 return {'captcha' : 'data:image/png;base64,' + captcha .decode ()}
0 commit comments