mirror of https://github.com/jumpserver/jumpserver
feat(login password ecrypt): 登录密码加密传输
parent
fdcda83c93
commit
71ee33e3be
|
@ -10,6 +10,7 @@ from users.utils import (
|
|||
)
|
||||
|
||||
reason_password_failed = 'password_failed'
|
||||
reason_password_decrypt_failed = 'password_decrypt_failed'
|
||||
reason_mfa_failed = 'mfa_failed'
|
||||
reason_mfa_unset = 'mfa_unset'
|
||||
reason_user_not_exist = 'user_not_exist'
|
||||
|
@ -19,6 +20,7 @@ reason_user_inactive = 'user_inactive'
|
|||
|
||||
reason_choices = {
|
||||
reason_password_failed: _('Username/password check failed'),
|
||||
reason_password_decrypt_failed: _('Password decrypt failed'),
|
||||
reason_mfa_failed: _('MFA failed'),
|
||||
reason_mfa_unset: _('MFA unset'),
|
||||
reason_user_not_exist: _("Username does not exist"),
|
||||
|
|
|
@ -10,7 +10,7 @@ class UserLoginForm(forms.Form):
|
|||
username = forms.CharField(label=_('Username'), max_length=100)
|
||||
password = forms.CharField(
|
||||
label=_('Password'), widget=forms.PasswordInput,
|
||||
max_length=128, strip=False
|
||||
max_length=1024, strip=False
|
||||
)
|
||||
|
||||
def confirm_login_allowed(self, user):
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form class="m-t" role="form" method="post" action="">
|
||||
<form id="form" class="m-t" role="form" method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<div style="line-height: 17px;">
|
||||
|
@ -26,7 +26,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
<input type="password" class="form-control" id="password" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
{% if form.errors.password %}
|
||||
<div class="help-block field-error">
|
||||
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<div>
|
||||
{{ form.captcha }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Login' %}</button>
|
||||
<button type="submit" class="btn btn-primary block full-width m-b" onclick="doLogin();return false;">{% trans 'Login' %}</button>
|
||||
|
||||
{% if demo_mode %}
|
||||
<p class="text-muted font-bold" style="color: red">
|
||||
|
@ -64,4 +64,17 @@
|
|||
{% endif %}
|
||||
|
||||
</form>
|
||||
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
|
||||
<script>
|
||||
function doLogin() {
|
||||
//公钥加密
|
||||
var rsaPublicKey = "{{ rsa_public_key }}"
|
||||
var password =$('#password').val(); //明文密码
|
||||
var jsencrypt = new JSEncrypt(); //加密对象
|
||||
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
||||
var passwordEncrypted = jsencrypt.encrypt(password); //加密
|
||||
$('#password').val(passwordEncrypted); //返回给密码输入input
|
||||
$('#form').submit();//post提交
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
<input type="password" class="form-control" id="password" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
{% if form.errors.password %}
|
||||
<div class="help-block field-error">
|
||||
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
||||
|
@ -109,7 +109,7 @@
|
|||
{{ form.captcha }}
|
||||
</div>
|
||||
<div class="form-group" style="margin-top: 10px">
|
||||
<button type="submit" class="btn btn-transparent">{% trans 'Login' %}</button>
|
||||
<button type="submit" class="btn btn-transparent" onclick="doLogin();return false;">{% trans 'Login' %}</button>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
<a href="{% url 'authentication:forgot-password' %}">
|
||||
|
@ -127,4 +127,18 @@
|
|||
</div>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
|
||||
<script>
|
||||
function doLogin() {
|
||||
//公钥加密
|
||||
var rsaPublicKey = "{{ rsa_public_key }}"
|
||||
var password =$('#password').val(); //明文密码
|
||||
var jsencrypt = new JSEncrypt(); //加密对象
|
||||
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
||||
var passwordEncrypted = jsencrypt.encrypt(password); //加密
|
||||
$('#password').val(passwordEncrypted); //返回给密码输入input
|
||||
$('#contact-form').submit();//post提交
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -1,9 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import base64
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from Crypto import Random
|
||||
from django.contrib.auth import authenticate
|
||||
|
||||
from common.utils import get_logger
|
||||
|
||||
from . import errors
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
def gen_key_pair():
|
||||
""" 生成加密key
|
||||
用于登录页面提交用户名/密码时,对密码进行加密(前端)/解密(后端)
|
||||
"""
|
||||
random_generator = Random.new().read
|
||||
rsa = RSA.generate(1024, random_generator)
|
||||
rsa_private_key = rsa.exportKey().decode()
|
||||
rsa_public_key = rsa.publickey().exportKey().decode()
|
||||
return rsa_private_key, rsa_public_key
|
||||
|
||||
|
||||
def rsa_decrypt(cipher_text, rsa_private_key=None):
|
||||
""" 解密登录密码 """
|
||||
if rsa_private_key is None:
|
||||
# rsa_private_key 为 None,可以能是API请求认证,不需要解密
|
||||
return cipher_text
|
||||
key = RSA.importKey(rsa_private_key)
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
message = cipher.decrypt(base64.b64decode(cipher_text.encode()), 'error').decode()
|
||||
return message
|
||||
|
||||
|
||||
def check_user_valid(**kwargs):
|
||||
password = kwargs.pop('password', None)
|
||||
|
@ -11,6 +41,15 @@ def check_user_valid(**kwargs):
|
|||
username = kwargs.pop('username', None)
|
||||
request = kwargs.get('request')
|
||||
|
||||
# 获取解密密钥,对密码进行解密
|
||||
rsa_private_key = request.session.get('rsa_private_key')
|
||||
if rsa_private_key is not None:
|
||||
try:
|
||||
password = rsa_decrypt(password, rsa_private_key)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return None, errors.reason_password_decrypt_failed
|
||||
|
||||
user = authenticate(request, username=username,
|
||||
password=password, public_key=public_key)
|
||||
if not user:
|
||||
|
|
|
@ -22,7 +22,7 @@ from common.utils import get_request_ip, get_object_or_none
|
|||
from users.utils import (
|
||||
redirect_user_first_login_or_index
|
||||
)
|
||||
from .. import forms, mixins, errors
|
||||
from .. import forms, mixins, errors, utils
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -108,9 +108,13 @@ class UserLoginView(mixins.AuthMixin, FormView):
|
|||
return self.form_class
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# 生成加解密密钥对,public_key传递给前端,private_key存入session中供解密使用
|
||||
rsa_private_key, rsa_public_key = utils.gen_key_pair()
|
||||
self.request.session['rsa_private_key'] = rsa_private_key
|
||||
context = {
|
||||
'demo_mode': os.environ.get("DEMO_MODE"),
|
||||
'AUTH_OPENID': settings.AUTH_OPENID,
|
||||
'rsa_public_key': rsa_public_key.replace('\n', '\\n')
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue