mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-05 02:56:31 +00:00
feat(login password ecrypt): 登录密码加密传输
This commit is contained in:
parent
fdcda83c93
commit
71ee33e3be
@ -10,6 +10,7 @@ from users.utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
reason_password_failed = 'password_failed'
|
reason_password_failed = 'password_failed'
|
||||||
|
reason_password_decrypt_failed = 'password_decrypt_failed'
|
||||||
reason_mfa_failed = 'mfa_failed'
|
reason_mfa_failed = 'mfa_failed'
|
||||||
reason_mfa_unset = 'mfa_unset'
|
reason_mfa_unset = 'mfa_unset'
|
||||||
reason_user_not_exist = 'user_not_exist'
|
reason_user_not_exist = 'user_not_exist'
|
||||||
@ -19,6 +20,7 @@ reason_user_inactive = 'user_inactive'
|
|||||||
|
|
||||||
reason_choices = {
|
reason_choices = {
|
||||||
reason_password_failed: _('Username/password check failed'),
|
reason_password_failed: _('Username/password check failed'),
|
||||||
|
reason_password_decrypt_failed: _('Password decrypt failed'),
|
||||||
reason_mfa_failed: _('MFA failed'),
|
reason_mfa_failed: _('MFA failed'),
|
||||||
reason_mfa_unset: _('MFA unset'),
|
reason_mfa_unset: _('MFA unset'),
|
||||||
reason_user_not_exist: _("Username does not exist"),
|
reason_user_not_exist: _("Username does not exist"),
|
||||||
|
@ -10,7 +10,7 @@ class UserLoginForm(forms.Form):
|
|||||||
username = forms.CharField(label=_('Username'), max_length=100)
|
username = forms.CharField(label=_('Username'), max_length=100)
|
||||||
password = forms.CharField(
|
password = forms.CharField(
|
||||||
label=_('Password'), widget=forms.PasswordInput,
|
label=_('Password'), widget=forms.PasswordInput,
|
||||||
max_length=128, strip=False
|
max_length=1024, strip=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def confirm_login_allowed(self, user):
|
def confirm_login_allowed(self, user):
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form class="m-t" role="form" method="post" action="">
|
<form id="form" class="m-t" role="form" method="post" action="">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
<div style="line-height: 17px;">
|
<div style="line-height: 17px;">
|
||||||
@ -26,7 +26,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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 %}
|
{% if form.errors.password %}
|
||||||
<div class="help-block field-error">
|
<div class="help-block field-error">
|
||||||
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
<div>
|
<div>
|
||||||
{{ form.captcha }}
|
{{ form.captcha }}
|
||||||
</div>
|
</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 %}
|
{% if demo_mode %}
|
||||||
<p class="text-muted font-bold" style="color: red">
|
<p class="text-muted font-bold" style="color: red">
|
||||||
@ -64,4 +64,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</form>
|
</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 %}
|
{% endblock %}
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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 %}
|
{% if form.errors.password %}
|
||||||
<div class="help-block field-error">
|
<div class="help-block field-error">
|
||||||
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
<p class="red-fonts">{{ form.errors.password.as_text }}</p>
|
||||||
@ -109,7 +109,7 @@
|
|||||||
{{ form.captcha }}
|
{{ form.captcha }}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" style="margin-top: 10px">
|
<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>
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<a href="{% url 'authentication:forgot-password' %}">
|
<a href="{% url 'authentication:forgot-password' %}">
|
||||||
@ -127,4 +127,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</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>
|
</html>
|
||||||
|
|
||||||
|
@ -1,9 +1,39 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- 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 django.contrib.auth import authenticate
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
|
||||||
from . import errors
|
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):
|
def check_user_valid(**kwargs):
|
||||||
password = kwargs.pop('password', None)
|
password = kwargs.pop('password', None)
|
||||||
@ -11,6 +41,15 @@ def check_user_valid(**kwargs):
|
|||||||
username = kwargs.pop('username', None)
|
username = kwargs.pop('username', None)
|
||||||
request = kwargs.get('request')
|
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,
|
user = authenticate(request, username=username,
|
||||||
password=password, public_key=public_key)
|
password=password, public_key=public_key)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -22,7 +22,7 @@ from common.utils import get_request_ip, get_object_or_none
|
|||||||
from users.utils import (
|
from users.utils import (
|
||||||
redirect_user_first_login_or_index
|
redirect_user_first_login_or_index
|
||||||
)
|
)
|
||||||
from .. import forms, mixins, errors
|
from .. import forms, mixins, errors, utils
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -108,9 +108,13 @@ class UserLoginView(mixins.AuthMixin, FormView):
|
|||||||
return self.form_class
|
return self.form_class
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
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 = {
|
context = {
|
||||||
'demo_mode': os.environ.get("DEMO_MODE"),
|
'demo_mode': os.environ.get("DEMO_MODE"),
|
||||||
'AUTH_OPENID': settings.AUTH_OPENID,
|
'AUTH_OPENID': settings.AUTH_OPENID,
|
||||||
|
'rsa_public_key': rsa_public_key.replace('\n', '\\n')
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
73
apps/static/js/plugins/jsencrypt/jsencrypt.min.js
vendored
Normal file
73
apps/static/js/plugins/jsencrypt/jsencrypt.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user