perf: password 等使用 rsa 加密传输 (#8188)

* perf: 修改 model fields 路径

* stash it

* pref: 统一加密方式,密码字段采用 rsa 加密

* pref: 临时密码使用 rsa

* perf: 去掉 debug msg

* perf: 去掉 Debug

* perf: 去掉 debug

* perf: 抽出来

Co-authored-by: ibuler <ibuler@qq.com>
This commit is contained in:
fit2bot
2022-05-07 16:20:12 +08:00
committed by GitHub
parent 3f856e68f0
commit 031077c298
43 changed files with 291 additions and 245 deletions

View File

@@ -23,9 +23,7 @@ from acls.models import LoginACL
from users.models import User
from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil
from . import errors
from .utils import rsa_decrypt, gen_key_pair
from .signals import post_auth_success, post_auth_failed
from .const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
logger = get_logger(__name__)
@@ -91,46 +89,8 @@ def authenticate(request=None, **credentials):
auth.authenticate = authenticate
class PasswordEncryptionViewMixin:
request = None
def get_decrypted_password(self, password=None, username=None):
request = self.request
if hasattr(request, 'data'):
data = request.data
else:
data = request.POST
username = username or data.get('username')
password = password or data.get('password')
password = self.decrypt_passwd(password)
if not password:
self.raise_password_decrypt_failed(username=username)
return password
def raise_password_decrypt_failed(self, username):
ip = self.get_request_ip()
raise errors.CredentialError(
error=errors.reason_password_decrypt_failed,
username=username, ip=ip, request=self.request
)
def decrypt_passwd(self, raw_passwd):
# 获取解密密钥,对密码进行解密
rsa_private_key = self.request.session.get(RSA_PRIVATE_KEY)
if rsa_private_key is None:
return raw_passwd
try:
return rsa_decrypt(raw_passwd, rsa_private_key)
except Exception as e:
logger.error(e, exc_info=True)
logger.error(
f'Decrypt password failed: password[{raw_passwd}] '
f'rsa_private_key[{rsa_private_key}]'
)
return None
class CommonMixin:
request: Request
def get_request_ip(self):
ip = ''
@@ -139,26 +99,6 @@ class PasswordEncryptionViewMixin:
ip = ip or get_request_ip(self.request)
return ip
def get_context_data(self, **kwargs):
# 生成加解密密钥对public_key传递给前端private_key存入session中供解密使用
rsa_public_key = self.request.session.get(RSA_PUBLIC_KEY)
rsa_private_key = self.request.session.get(RSA_PRIVATE_KEY)
if not all([rsa_private_key, rsa_public_key]):
rsa_private_key, rsa_public_key = gen_key_pair()
rsa_public_key = rsa_public_key.replace('\n', '\\n')
self.request.session[RSA_PRIVATE_KEY] = rsa_private_key
self.request.session[RSA_PUBLIC_KEY] = rsa_public_key
kwargs.update({
'rsa_public_key': rsa_public_key,
})
return super().get_context_data(**kwargs)
class CommonMixin(PasswordEncryptionViewMixin):
request: Request
get_request_ip: Callable
def raise_credential_error(self, error):
raise self.partial_credential_error(error=error)
@@ -193,20 +133,13 @@ class CommonMixin(PasswordEncryptionViewMixin):
user.backend = self.request.session.get("auth_backend")
return user
def get_auth_data(self, decrypt_passwd=False):
def get_auth_data(self, data):
request = self.request
if hasattr(request, 'data'):
data = request.data
else:
data = request.POST
items = ['username', 'password', 'challenge', 'public_key', 'auto_login']
username, password, challenge, public_key, auto_login = bulk_get(data, items, default='')
ip = self.get_request_ip()
self._set_partial_credential_error(username=username, ip=ip, request=request)
if decrypt_passwd:
password = self.get_decrypted_password()
password = password + challenge.strip()
return username, password, public_key, ip, auto_login
@@ -482,10 +415,10 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
need = cache.get(self.key_prefix_captcha.format(ip))
return need
def check_user_auth(self, decrypt_passwd=False):
def check_user_auth(self, valid_data=None):
# pre check
self.check_is_block()
username, password, public_key, ip, auto_login = self.get_auth_data(decrypt_passwd)
username, password, public_key, ip, auto_login = self.get_auth_data(valid_data)
self._check_only_allow_exists_user_auth(username)
# check auth
@@ -537,11 +470,12 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
self.mark_password_ok(user, False)
return user
def check_user_auth_if_need(self, decrypt_passwd=False):
def get_user_or_auth(self, valid_data):
request = self.request
if not request.session.get('auth_password'):
return self.check_user_auth(decrypt_passwd=decrypt_passwd)
return self.get_user_from_session()
if request.session.get('auth_password'):
return self.get_user_from_session()
else:
return self.check_user_auth(valid_data)
def clear_auth_mark(self):
keys = ['auth_password', 'user_id', 'auth_confirm', 'auth_ticket_id']