我的网站搭建(第56天) 用户登录注册信息加密

  • 发布时间:2017年6月27日 03:00
  • 作者:杨仕航
* 该文是基于Python2.7开发的,最新Python3.x和Django2.x视频教程可以前往 >> Django2.0视频教程

一直以来在我的网站中,对于登录和注册提交的数据都是明文提交。也就是说,如果随便监听网络,可以直接看到密码的明文。这样是很不安全。最好对密码这些敏感信息加密再提交。以登录为例,前端代码(简化)如下:

<form id="user_form" class="main_form" method='post'>
    {%csrf_token%}

    <div id="user_control">
        <div class="control-group">
            <label for="user_name">邮箱:</label>
            <input type="text" id="user_name" name="user_name"/>
        </div>

        <div class="control-group">
            <label for="user_pwd">密码:</label>
            <input type="password" id="user_pwd" name="user_pwd"/>
        </div>
    </div>   

    <p id="tip_text" style="color:red;"></p>

    <div class="form_buttons">
        <input type="submit" value="登录"/>
    </div>
</form>


提交表单代码如下,我写成ajax异步提交的方式:

<script type="text/javascript">
    $(function(){
        //登录
        $('#user_form').submit(function(){
            //验证
            var tip=$('#tip_text');
            tip.text('');

            if($('#user_name').val()==''){
                tip.text('请输入邮箱');
                return false;
            };
            if($('#user_pwd').val()==''){
                tip.text('请输入密码');
                return false;
            };

            //登录
            $.ajax({
                type: "POST",
                data: $('#user_form').serialize(),
                url: "{% url 'user_login' %}",
                cache: false,
                dataType: "json",
                success: function(json, textStatus){
                    var is_success = json['success'];
                    if(is_success){
                        tip.text('登录成功,页面处理中...');
                        window.location.href = "/";
                    }else{
                        tip.text('邮箱或者密码错误,请重试');
                    };
                },
                error: function (XMLHttpRequest, textStatus, errorThrown){
                    tip.text("登录出错,请重试 "+errorThrown);
                }
            });
            return false;
        });
    });
</script>


其中,{% url 'user_login' %}设置url对应的响应方法如下:

#coding:utf-8
from django.http import HttpResponse
from django.contrib.auth import authenticate, login
import json

def user_login(request):
    """login"""
    response_data = {}

    try:
        login_name = request.POST.get('user_name')
        login_pwd = request.POST.get('user_pwd')

        if len(login_name)*len(login_pwd)==0:
            raise Exception(u"邮箱或密码为空")

        user = authenticate(username=login_name, password=login_pwd)
        if user is not None:
            login(request, user)
        else:
            raise Exception(u"邮箱或密码不正确")

        response_data['success'] = True
        response_data['message'] = 'ok'

    except e:
        response_data['success'] = False
        response_data['message'] = e.message
    finally:
        return HttpResponse(json.dumps(response_data), 
                            content_type="application/json")


加密方法有很多中,比较安全的加密方法是非对称加密RSA算法。也就是常见的公钥和私钥,公钥加密、私钥解密。公钥公开无所谓,如果没有对应的私钥是很难解密得到正确的内容。

那么,我们可以生成一对公钥和私钥。公钥给前端页面,私钥保存到settings中。前端再将用户名和密码组合之后加密,提交给服务器。服务器得到密文再根据私钥解密得到用户名和密码。再进一步验证、登录等操作。

思路理顺之后,开始安装相关的库。rsa算法有个rsa库,但这个得到的公钥和私钥有问题。自己可以使用,但给其他语言写的rsa算法却无法使用。建议安装pycrypto库,而且要安装最新版(我使用的是2.6.1版)。pycrypto的旧版没有rsa算法。执行如下命令安装:

pip install pycrypto


若安装出错,检查是否有权限以及是否有python-dev开发包。若没有,执行yum或apt-get安装python-dev。

安装完成之后,先生成一对密钥。进入Python命令行模式,执行如下代码:

#coding:utf-8
from Crypto import Random
from Crypto.Hash import SHA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from Crypto.PublicKey import RSA

# 伪随机数生成器
random_generator = Random.new().read
# rsa算法生成实例
rsa = RSA.generate(1024, random_generator)

# master的秘钥对的生成
private_pem = rsa.exportKey()
public_pem = rsa.publickey().exportKey()

pub = public_pem.decode()
pri = private_pem.decode()


再分别print输出pub和pri:

print(pub)
print(pri)


分别可以得到公钥和私钥,复制并粘贴保存到settings.py文件中。例如(这个当然不是我使用的公钥和私钥,私钥一定要保护好):

PUBLIC_KEY = '''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3CKmYdAJXXdg18UdkndZNppW8
hhtJCbLy/sLMLGnUFdPg8hxIBHGigWqca+4R8DEUHizvM8LAuR4fCjHVodYEZyeb
cpGeyXp06Cnrp5zEeRlbRJd2iUiBzTMNgTUCgPSkisiI6QTVKF1C6kafr5NAhYQg
4wKLCBiCmL58S9N+FwIDAQAB
-----END PUBLIC KEY-----
'''

PRIVATE_KEY = '''-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQD3CKmYdAJXXdg18UdkndZNppW8hhtJCbLy/sLMLGnUFdPg8hxI
BHGigWqca+4R8DEUHizvM8LAuR4fCjHVodYEZyebcpGeyXp06Cnrp5zEeRlbRJd2
iUiBzTMNgTUCgPSkisiI6QTVKF1C6kafr5NAhYQg4wKLCBiCmL58S9N+FwIDAQAB
AoGBAJa0HSqZUzhTUuyNll1GgQ98GgsZ0Rl8pkPhsZr3WVR+QHURnrk8pMpbo1tr
6J/e5UH9qrH+5MfH5x1OjdpjUXjJVAoQgvWgxGPN5MwMli9/tEooFIM/m3SNaHpt
1ivCPDCnu1oi7ptB0pSyaeLevK35x6BpQRwlfF+Qfz8PSjExAkEA+uG9dpYPegDs
BqDMm6OFcn5TCUsIUcWItlnRWg1TJgOY6KGp2Omn9Jx8vah0KrUSdQIEFi9xBC1G
/QYqb9x8iQJBAPwS03KhgAjXueafSFZoUBPrOyv/Vfe+4PuQpQ1NVmOHRJUiaMV0
ah/VjhizXl/XjFipB2opyI7zgVg6DFgpvZ8CQESF26XEHr4H+m9lA/2OkChRcISd
Rcxv04NazN93vDopyV3gqe9hAkrBYyC6HeB8TGpA/rTAiHHHnBgH0xdydWECQGnK
uOWGNEcByuqodOKW2uzRL79SZVEZ0jCiTzMa/yO8VKNGkrQ0HIoJxZ5GQVh2DVf0
cME0khC0z9coXZ/uvL8CQC+AtWk2QnxoSaV5v06kxH9WzOtQtoPQEVV4qvP4ftpR
MJp7cjM4R1jrpfhoCk5rokizESoxavjxa4qZOMad07Y=
-----END RSA PRIVATE KEY-----
'''


这样,我们就可以通过from django.conf import settings读取这对密钥。

公钥可以在渲染登录页面,作为变量给前端。这个很简单,不用我多说吧?好吧,我还是简单写一下吧:

from django.conf import settings
from django.shortcuts import render

def login_page(request):
    data = {}
    data['public_key'] = settins.PUBLIC_KEY
    return render(request, 'login.html', data)


接下来,需要修改登录页面模板(login.html)。不过在这之前,先找个rsa算法的javascript库:jsencrypt

从github下载下来,找到bin/jsencrypt.min.js并放到你的django项目中。

我们利用该库实现前端的rsa加密。修改前端页面如下:

<!--引入jsencrypt,具体路径和位置自行修改-->
<script src="/static/js/jsencrypt.min.js"></script>


<form id="user_form" class="main_form" method='post'>
    {%csrf_token%}
    <!--公钥-->
    <input id="public_key" type="hidden" value="{{public_key}}" name="public_key">
    
    <div id="user_control">
        <div class="control-group">
            <label for="user_name">邮箱:</label>
            <input type="text" id="user_name" name="user_name"/>
        </div>

        <div class="control-group">
            <label for="user_pwd">密码:</label>
            <input type="password" id="user_pwd" name="user_pwd"/>
        </div>
    </div>   

    <p id="tip_text" style="color:red;"></p>

    <div class="form_buttons">
        <input type="submit" value="登录"/>
    </div>
</form>


之前的登录代码也要修改,对用户名和密码加密:

<script type="text/javascript">
    $(function(){
        //登录
        $('#user_form').submit(function(){
            //验证
            var tip=$('#tip_text');
            tip.text('');

            if($('#user_name').val()==''){
                tip.text('请输入邮箱');
                return false;
            };
            if($('#user_pwd').val()==''){
                tip.text('请输入密码');
                return false;
            };
            
            //对用户名和密码合并,再rsa加密
            var content = $("#user_name").val() + '|' + $("#user_pwd").val();
            var encrypt = new JSEncrypt();
            encrypt.setPublicKey($('#public_key').val());
            var encrypted = encrypt.encrypt(content);

            //组合POST的数据
            var data = {};
            data["csrfmiddlewaretoken"] = $("input[name=csrfmiddlewaretoken]").val();
            data["encrypt_text"] = encrypted;

            //登录
            $.ajax({
                type: "POST",
                data: $.param(data),
                url: "{% url 'user_login' %}",
                cache: false,
                dataType: "json",
                success: function(json, textStatus){
                    var is_success = json['success'];
                    if(is_success){
                        tip.text('登录成功,页面处理中...');
                        window.location.href = "/";
                    }else{
                        tip.text('邮箱或者密码错误,请重试');
                    };
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    tip.text("登录出错,请重试 "+errorThrown);
                }
            });
            return false;
        });
    });
</script>


由于,无需也不能包含用户名和密码信息到POST的数据。所以重新组合新数据并POST提交。

这时,再修改后端代码,对密文解密并处理。如下修改的后端登录代码:

#coding:utf-8
from Crypto import Random
from Crypto.Hash import SHA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from Crypto.PublicKey import RSA
import base64

from django.http import HttpResponse
from django.contrib.auth import authenticate, login
from django.conf import settings
import json

def user_login(request):
    """login"""
    response_data = {}

    try:
        encrypt_text = request.POST.get('encrypt_text')
        # 伪随机数生成器
        random_generator = Random.new().read
        # rsa算法生成实例
        rsa = RSA.generate(1024, random_generator)
        rsakey = RSA.importKey(settings.PRIVATE_KEY)
        cipher = Cipher_pkcs1_v1_5.new(rsakey)
        
        # 解密
        text = cipher.decrypt(base64.b64decode(encrypt_text), random_generator)
        # 根据竖线拆分得到用户名和密码
        login_name, login_pwd = text.split('|')
        if len(login_name)*len(login_pwd)==0:
            raise Exception(u"邮箱或密码为空")

        user = authenticate(username=login_name, password=login_pwd)
        if user is not None:
            login(request, user)
        else:
            raise Exception(u"邮箱或密码不正确")

        response_data['success'] = True
        response_data['message'] = 'ok'

    except e:
        response_data['success'] = False
        response_data['message'] = e.message
    finally:
        return HttpResponse(json.dumps(response_data), content_type="application/json")


到这里,就完成rsa前端对用户名和密码加密提到到后端。后端再加密,得到原文。再继续执行之前的操作。

同样,注册和修改密码等页面处理方式一样。我就不再赘述。

上一篇:scp远程传输文件命令

下一篇:Django模板设置全局变量(默认变量)

评论列表

jiawei6636

jiawei6636

2020-07-21 18:52 回复

新的评论

清空