Django 20购物商城项目(注册、登录页面:生成动态验证码)

dDjango 20购物商城项目

      • 1、安装pillow
      • 2、在注册页面加入验证码
        • 2.1、register.html (增加内容)
        • 2.2、register.js(增加内容)
        • 2.2、路由、视图(增加内容)
          • 2.2.1、路由
          • 2.2.2、视图
            • views_heiper.py(增加两个函数)
            • views.py
            • 点击注册后对比验证码
      • 3、这里我把注册时密码验证、页面返回等完善了一下
        • 3.1、增加了邮箱格式验证
        • 3.2、增加了密码格式验证
      • 4、登陆页面验证码

这里只展示代码,一些原理和细节可以参考我这篇博客

https://blog.csdn.net/a__int__/article/details/103354549

1、安装pillow

pillow是一个生成图像的库

pip install Pillow

2、在注册页面加入验证码

2.1、register.html (增加内容)

这里我加在了“注册”按钮之前
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第1张图片

            <span>验证码:span><input id="verify_code" type="text" name="verify_code" placeholder="请输入图中的验证码">
            <img src="{% url 'axf:get_code' %}">
            <br>
            {% if verify_wrong %}
                <span id="verify_span" style="color: red">{{ verify_wrong }}span>
            {% else %}
                <span id="verify_span" style="color: green">span>
            {% endif %}
            <br>
            <br>

2.2、register.js(增加内容)

点击图片更换
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第2张图片

    $("img").click(function () {
        // 在浏览器里面路径不改变,这个图他就会不发生改变,所以给这个路径加一个随机数让浏览器知道改变了
        $(this).attr("src","/axf/getcode/?t="+Math.random());
        $("#verify_span").html("").css("color", "green");
    })

2.2、路由、视图(增加内容)

2.2.1、路由

Django 20购物商城项目(注册、登录页面:生成动态验证码)_第3张图片

2.2.2、视图
views_heiper.py(增加两个函数)
# 验证码,随机颜色
def get_color():
    return random.randrange(256)


# 验证码,随机4个文字
def get_txt():
    txt = "zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP1234567890"
    code = ""
    for i in range(4):
        code + random.choice(txt)
    return code
views.py

视图里新导入的库
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第4张图片
增加函数get_code()

绘制:画板、文字、半圆弧

Django 20购物商城项目(注册、登录页面:生成动态验证码)_第5张图片

def get_code(request):
    """
    Image 画板     ImageDraw 画笔

    """
    color_bg = (get_color(), get_color(), get_color())
    # 设置画板的背景及大小
    image = Image.new(mode='RGB', size=(100, 50), color=color_bg)
    # 画笔绘制
    imagedraw = ImageDraw(image, mode='RGB')
    # 获取随机文字
    the_code = get_txt()
    # 把所有小写字母换成大写字母,存入session
    # 验证时同样大小写转换一遍,这样用户输入时可以不区分大小写
    upper_code = the_code.upper()
    request.session["the_code"] = upper_code
    font = ImageFont.truetype("C:\Windows\Fonts\simsunb.ttf", 50)
    for i in range(4):
        # 文字填充色
        fill = (get_color(), get_color(), get_color())
        # 画笔在画板上绘制文字
        imagedraw.text(xy=(25 * i, 0), text=the_code[i], fill=fill, font=font)

    # 绘制1000个杂色点
    for i in range(1000):
        fill = (get_color(), get_color(), get_color())
        xy = (random.randrange(201), random.randrange(100))
        imagedraw.point(xy=xy, fill=fill)

    # 绘制向下的半圆弧
    """
    drawObject.arc([x1, y1, x2, y2], startAngle, endAngle, options)
    在左上角坐标为(x1, y1),右下角坐标为(x2, y2)
    矩形区域内满圆O内,以starangle为起始角度,endAngle为终止角度,截取圆O的一部分圆弧画出来
    """
    imagedraw.arc((random.randrange(0,10),6,80,random.randrange(70,100)), random.randrange(0,180),random.randrange(250,360), fill=get_color())

    fp = BytesIO()
    image.save(fp, "png")

    return HttpResponse(fp.getvalue(), content_type="image/png")

效果截图
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第6张图片

点击注册后对比验证码

Django 20购物商城项目(注册、登录页面:生成动态验证码)_第7张图片

def register(request):
    # 请求
    if request.method == "GET":
        data = {
            "title": "注册"
        }
        return render(request, 'user/register.html', context=data)

    # 提交
    elif request.method == "POST":

        # 用户输入的验证码
        verify_code = request.POST.get("verify_code")
        verify_code = verify_code.upper()
        print("获取到验证码le")
        # session存的验证码
        the_code = request.session.get("the_code")
        if verify_code != the_code:
            data = {
                "verify_wrong": "验证码不对,重新输入"
            }
            return render(request, 'user/register.html', context=data)
        else:
            try:
                username = request.POST.get("username")
                email = request.POST.get("email")
                password = request.POST.get("password")
                icon = request.FILES.get("icon")
                password = make_password(password)
                user = AXFUser()
                user.u_name = username
                user.u_password = password
                user.u_icon = icon
                user.u_email = email
                user.save()

                # 用uuid得到唯一激活码,再哈希加密
                u_token = uuid.uuid4().hex
                # 缓存过期时间为1天
                cache.set(u_token, user.id, timeout=60 * 60 * 24)

                send_email_activate(username, email, u_token)
            except Exception as err:
                data = {
                    "err": "数据异常"
                }
                return render(request, 'user/register.html', context=data)
        return redirect(reverse("axf:login"))

3、这里我把注册时密码验证、页面返回等完善了一下

下面是register.js目前完整代码

$(function () {

    T_change("#email_input", "#email_info", "邮箱可以用", "邮箱已存在", T_path = '/axf/checkemail/');
    T_change("#username_input", "#username_info", "用户名可用", "用户名已存在", T_path = '/axf/checkuser/');
    T_change("#password_input", "#password_info", "密码可用", "密码必须含有字母、数字、符号(+-.%#@*&!)三种", T_path = "/axf/checkpwd/");
    T_change("#password_confirm_input", "#password_confirm_info", "", "", T_path = "", is_password = 1);

    $("img").click(function () {
        // 在浏览器里面路径不改变,这个图他就会不发生改变,所以给这个路径加一个随机数让浏览器知道改变了
        $(this).attr("src", "/axf/getcode/?t=" + Math.random());
        $("#verify_span").html("").css("color", "green");
    });

});

function T_change(T_input, T_info, T_text1, T_text2, T_path = "", is_password = 0) {
    var $T_i = $(T_input);
    $T_i.change(function () {

        // val()返回被选元素的当前值,trim()的作用是去掉字符串两端的多余的空格
        var T_i = $T_i.val().trim();
        console.log(T_i);
        if (is_password === 1) {
            var $T_i_info = $(T_info);
            $T_i_info.html(T_text1).css("color", "green");
        } else {
            if (T_i.length) {
                // 将用户名发给服务器
                $.getJSON(T_path, {"T_name": T_i}, function (data) {
                    var $T_i_info = $(T_info);
                    if (data['status'] === 200) {
                        $T_i_info.html(T_text1).css("color", "green");
                    } else if (data['status'] === 901) {
                        $T_i_info.html(T_text2).css("color", "red");
                    } else if (data['status'] === 902) {
                        $T_i_info.html(data['msg']).css("color", "red");
                    }

                })
            }
        }
    })
}

function check() {

    username = check_one("#username_input", "#username_info", "用户名不能为空");
    email_o = check_one("#email_input", "#email_info", "邮箱不能为空");
    password_o = check_one("#password_input", "#password_info", "密码不能为空");
    password_t = check_one("#password_confirm_input", "#password_confirm_info", "请再次输入密码");

    var $password_input = $("#password_input");
    var password = $password_input.val().trim();

    var $password_confirm = $("#password_confirm_input");
    var password_confirm = $password_confirm.val().trim();

    var $email_input = $("#email_input");
    var email = $email_input.val().trim();

    // 密码输入后md5加密后提交(注意:这里是前端加密,传入后台的时候还会再加密一次)
    if (password_o === 0 || password_t === 0) {
        if (password === password_confirm) {
            console.log("两次密码输入一致");
            if (password === email) {
                $("#password_info").html("密码不能和邮箱一样").css("color", "red");
                $("#password_confirm_info").html("密码不能和邮箱一样").css("color", "red");
                return false
            } else {
                $password_input.val(hex_md5(password));
                $password_confirm.val(hex_md5(password));
                $("#password_confirm_info").html("").css("color", "green");
            }
        } else {
            $("#password_confirm_info").html("两次密码输的不一样").css("color", "red");
            return false
        }
    }


    return !(username + email_o + password_o);
}

function check_one(C_input, C_info, C_text) {

    var $username_info = $(C_info);
    var $username = $(C_input);

    var username = $username.val().trim();
    var info_color = $username_info.css("color");

    // 检查是否为空
    if (!username) {
        $username_info.html(C_text).css("color", "red");
        return 1
    }
    // 检查是否有重复
    if (info_color === 'rgb(255, 0, 0)' || info_color === 'rgb(51, 51, 51)') {
        console.log(C_input);
        console.log("颜色不对");
        return 1
    }

    return 0
}

下面是register.html目前完整代码

{% extends "base_user.html" %}
{% load static %}
{% block ext_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'axf/user/css/register.css' %}">
{% endblock %}
{% block ext_js %}
    {{ block.super }}
    <script type="text/javascript" src="{% static 'axf/user/js/register.js' %}">script>
{% endblock %}
{% block footer %}
    <div class="title">
        <a href="#" onclick="javascript:history.back(-1);">
            <div class="arrow-box nav-left">
                返回
            div>
        a>
        <p class="font">用户注册p>
    div>
{% endblock %}
{% block content %}
    <div class="container">
        {% if err %}
            <span>{{ err }}span>
        {% endif %}
        <form method="POST" enctype="multipart/form-data" action="{% url 'axf:register' %}" onsubmit="return check()">
            {% csrf_token %}
            <div class="form-group">
                <label for="username_input" id="label_red">用户名label>
                <input name="username" type="text" class="form-control" id="username_input" placeholder="请输入用户名">
                <span id="username_info">span>
            div>
            <div class="form-group">
                <label for="email_input" id="label_red"> 邮箱label>
                <input name="email" type="text" class="form-control" id="email_input" placeholder="请输入邮箱">
                <span id="email_info">span>
            div>
            <div class="form-group">
                <label for="password_input" id="label_red">密码label>
                <input name="password" type="password" class="form-control" id="password_input" placeholder="请输入密码">
                <span id="password_info">span>
            div>
            <div class="form-group">
                <label for="password_confirm_input" id="label_red">确认密码label>
                <input type="password" class="form-control" id="password_confirm_input" placeholder="请再次输入密码">
                <span id="password_confirm_info">span>
            div>
            <div class="form-group">
                <label for="icon_input"> 头像label>
                <input name="icon" type="file" id="icon_input" placeholder="请再次输入密码">

            div>
            <br>
            <span>验证码:span><input id="verify_code" type="text" name="verify_code" placeholder="请输入图中的验证码">
            <img src="{% url 'axf:get_code' %}">
            <br>
            {% if verify_wrong %}
                <span id="verify_span" style="color: red">{{ verify_wrong }}span>
            {% else %}
                <span id="verify_span" style="color: green">span>
            {% endif %}
            <br>
            <br>
            <button type="submit" class="btn btn-success btn-block">注册button>
        form>
        <br>
        <br>
        <br>
        <br>
    div>
{% endblock %}

下面是register.css目前完整代码

header{
    height: 0rem;
    margin-bottom: 0rem;
}

.title {
    top:0;
    position: fixed;
    width: 100%;
    height: 50px;
    background-color: #009688;
}

.font {
    color: #fff;
    line-height: 50px;
    font-size: 20px;
    text-indent: -2em;
    text-align: center
}

.nav-left {
    float: left;
}

.arrow-box{
    width: 50px;
    height: 26px;
    position: relative;
    border-radius: 10% 10%;
    background: #fff;
    text-align: center;
    line-height: 26px;
    top: 12px;
    font-size: 14px;
    left: 10px;
}
#label_red:before {
    content: "*";
    color: red;
}
.container{
    margin-top: 2rem;
}

页面截图
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第8张图片

3.1、增加了邮箱格式验证

视图里的 def check_email(request)
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第9张图片

def check_email(request):
    email = request.GET.get("T_name")
    e = AXFUser.objects.filter(u_email=email)
    data = {
        "status": HTTP_OK,
        "msg": '邮箱可用',
    }
    # exists()判断路径是否存在
    if e.exists():
        data["status"] = HTTP_USER_EXIST
        data["msg"] = '邮箱已经存在'
    else:

        # 邮箱规则[email protected] 最后一个x需为com,.cn,.net,.org..等
        if re.match("[A-Za-z0-9]+@[A-Za-z0-9]+\.[cmnoetrg]+", email):
            pass
        else:
            data["status"] = 902
            data["msg"] = '邮箱格式不正确'
    return JsonResponse(data=data)

3.2、增加了密码格式验证

新增路由
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第10张图片
视图新增函数
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第11张图片


def check_pwd(request):
    pwd = request.GET.get("T_name")
    data = {}
    l_re = re.compile('[a-zA-Z]')
    num_re = re.compile('[0-9]')
    p_re = re.compile('[\.\?\!\=\-\+\_\|\*\&\%\#\@\~\@\!\%\¥]')
    wrong_re = re.compile('[^a-zA-Z0-9\.\?\!\=\-\+\_\|\*\&\%\#\@\~\@\!\%\¥]')

    if len(pwd) < 6 or len(pwd) > 20:
        data["status"] = 902
        data["msg"] = '请输入6到20位的密码'
    elif wrong_re.search(pwd) != None:
        data["status"] = 902
        data["msg"] = '密码中包含了无效字符'
    else:
        if l_re.search(pwd) == None:
            data["status"] = 902
            data["msg"] = '密码里必须包含字母'
        elif num_re.search(pwd) == None:
            data["status"] = 902
            data["msg"] = '密码里必须包含数字'
        elif p_re.search(pwd) == None:
            data["status"] = 902
            data["msg"] = '密码里必须包含.?!=-+_|*&%#@~¥中至少一个符号'
        else:
            data["status"] = 200
            data["msg"] = '该密码可以使用'

    return JsonResponse(data=data)

4、登陆页面验证码

login.html(增加了三处:css样式新增、头部、验证码,代码和注册页面里的一样)
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第12张图片
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第13张图片
login.css(新建文件,内容和注册页面的css一模一样)
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第14张图片

header{
    height: 0rem;
    margin-bottom: 0rem;
}

.title {
    top:0;
    position: fixed;
    width: 100%;
    height: 50px;
    background-color: #009688;
}

.font {
    color: #fff;
    line-height: 50px;
    font-size: 20px;
    text-indent: -2em;
    text-align: center
}

.nav-left {
    float: left;
}

.arrow-box{
    width: 50px;
    height: 26px;
    position: relative;
    border-radius: 10% 10%;
    background: #fff;
    text-align: center;
    line-height: 26px;
    top: 12px;
    font-size: 14px;
    left: 10px;
}
#label_red:before {
    content: "*";
    color: red;
}
.container{
    margin-top: 2rem;
}

视图(和注册页面的视图函数基本一样)
Django 20购物商城项目(注册、登录页面:生成动态验证码)_第15张图片
这是登录视图函数完整代码

def login(request):
    if request.method == "GET":
        error_msg = request.session.get('error_msg')
        data = {
            "title": "登录"
        }
        if error_msg:
            # 如果session['error_msg']存在就删除,并将error_msg显示在页面上
            del request.session['error_msg']
            data['error_msg'] = error_msg
        return render(request, 'user/login.html', context=data)

    elif request.method == "POST":
        # 用户输入的验证码
        verify_code = request.POST.get("verify_code")
        # 把小写字母全部换成大写字母
        verify_code = verify_code.upper()
        print("获取到验证码le")
        # session存的验证码
        the_code = request.session.get("the_code")
        if verify_code != the_code:
            data = {
                "verify_wrong": "验证码不对,重新输入"
            }
            return render(request, 'user/login.html', context=data)
        else:
            username = request.POST.get("username")
            password = request.POST.get("password")

            users = AXFUser.objects.filter(u_name=username)

            # exists()方法主要是判断查询的数据是否存在,存在时返回True,否则返回False
            # first() 返回queryset中匹配到的第一个对象,如果没有匹配到对象则为None

            if users.exists():
                user = users.first()
                if check_password(password, user.u_password):
                    if user.is_active:

                        request.session['user_id'] = user.id
                        return redirect(reverse('axf:mine'))
                    else:
                        request.session['error_msg'] = '未激活'
                        return redirect(reverse('axf:login'))
                else:
                    # 密码错误
                    request.session['error_msg'] = '密码错误'
                    return redirect(reverse('axf:login'))
            # 用户名不存在
            request.session['error_msg'] = '用户不存在'
        return redirect(reverse('axf:login'))

你可能感兴趣的:(web项目:生鲜购物商城)