Django---登陆(含随机生成图片验证码)、注册示例讲解

登陆(验证码)、注册功能具体代码

 # urls.py

from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),

    path("login/",views.login),  #登陆
    path("get_valid_img/",views.get_valid_img), #生成验证码
    path("reg/",views.reg)  #注册

]

 # views.py

# Create your views here.
import random
import re
from io import BytesIO

from PIL import Image, ImageDraw, ImageFont
from django import forms
from django.contrib import auth
from django.core.exceptions import ValidationError
from django.forms import widgets
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse

from app01.models import UserInfo


###################### 建立UserForm用户校验,输入的内容是否合法##############
class UserForm(forms.Form):
    user = forms.CharField(min_length=5, label='用户名')
    pwd = forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="密码")
    r_pwd = forms.CharField(min_length=5, widget=widgets.PasswordInput(), label="确认密码")
    email = forms.EmailField(min_length=5, label="邮箱")

    # 为每个字段 input 添加类名 form-control
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for filed in self.fields.values():
            filed.widget.attrs.update({'class': 'form-control'})

    def clean_user(self):
        val = self.cleaned_data.get('user')
        user = UserInfo.objects.filter(username=val).first()
        if user:
            raise ValidationError('用户名已存在!')
        else:
            return val

    def clean_pwd(self):
        val = (self.cleaned_data.get('pwd'))
        if val.isdigit():
            raise ValidationError('密码不能为纯数字!')
        else:
            return val

    def clean_email(self):
        val = self.cleaned_data.get('email')
        if re.search("\w+@163.com$", val):
            return val
        else:
            raise ValidationError('邮箱必须是163格式邮箱!')

    def clean(self):   # 全局校验
        pwd = self.cleaned_data.get('pwd')
        r_pwd = self.cleaned_data.get('r_pwd')
        if pwd and r_pwd and pwd != r_pwd:
            # raise ValidationError("两次密码不一致!")

            # 也能够采用如下方法,这样在渲染的时候能够不用特殊处理,全局的错误会在r_pwd中,再也不是__all__
            self.add_error("r_pwd", ValidationError('两次密码不一致!'))
        else:
            return self.cleaned_data


####################### 登陆  ######################
def login(request):
    if request.method == "GET":
        return render(request, "login.html")

    else:
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        validcode = request.POST.get("validcode")

        res = {"user": None, "error_msg": ""}
        if validcode.upper() == request.session.get("keep_str").upper():
            user_obj = auth.authenticate(username=user, password=pwd)
            if user_obj:
                res["user"] = user
            else:
                res["error_msg"] = "用户名或密码错误!"
        else:
            res["error_msg"] = "验证码错误"

        return JsonResponse(res)



#######################   生成验证码  ################################
def get_valid_img(request):
    def get_random_color():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # 生成图片
    img = Image.new("RGB", (250, 35), get_random_color())
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype("static/font/kumo.ttf", 32)

    keep_str = ''  # 用于存储验证码,用户后台校验

    # 为图片添加随机文本text
    for i in range(6):
        rand_num = str(random.randint(0, 9))
        rand_lowalp = chr(random.randint(97, 122))
        rand_upealp = chr(random.randint(65, 90))

        random_char = random.choice([rand_num, rand_lowalp, rand_upealp])

        draw.text((i * 30 + 50, 0), random_char, get_random_color(), font=font)
        keep_str += random_char

    # --------加噪点和噪线------
    width = 250
    height = 35
    for i in range(3):
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        draw.line((x1, y1, x2, y2), fill=get_random_color())

    for i in range(2):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())

    # 将生成的图片进行读写
    f = BytesIO()  # 写,在内存中建立文件句柄
    img.save(f, "png")  # 将img保存在句柄中

    data = f.getvalue()  # 读取图片字节,返回给img响应

    # 生成的随机字符串,加到sesstion中,传给用户,在用户登陆时候进行校验,不能将随机字符串做为全局变量
    request.session["keep_str"] = keep_str

    # 将图片字节 直接响应回图片
    return HttpResponse(data)



##########################  注册  ##########################
def reg(request):
    if request.method == "GET":
        userform = UserForm()
        return render(request, "reg.html", locals())
    else:
        form = UserForm(request.POST)

        res = {"user": None, "err_msg": ""}

        if form.is_valid():
            user = form.cleaned_data.get('user')
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')

            UserInfo.objects.create_user(username=user, password=pwd, email=email)

            res['user'] = user
        else:
            res['err_msg'] = form.errors

        return JsonResponse(res)

# login.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style type="text/css">
        body{
             background:url(/static/img/小桥.jpg);
             background-size:100%;
             background-attachment: fixed;
        }

    </style>
</head>
<body>
<h1 class="text-primary" style="text-align: center;line-height:150px">登陆页面</h1>

<div class="container">
    <div class="row">

        <div class="col-lg-4 col-md-offset-4">
{#            <form action="" method="post">#}
                {% csrf_token %}
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" class="form-control "  placeholder="用户名"  id="user">
                </div>
                 <div class="form-group">
                    <label for="">密码</label>
                    <input type="password" class="form-control"  placeholder="密码" id="pwd">
                </div>
                <div class="form-group">
                    <label for="">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" class="form-control" placeholder="验证码"  id="validcode">
                        </div>
                        <div class="col-md-6">
                           <img width="165" height="35" src="/get_valid_img/" alt="" id="img">
                        </div>
                    </div>
                </div>
                <input type="button" class="btn btn-primary btn-block pull-right login_btn" value="登陆">
                <p><a href="/reg/" style="text-align: center;display: block">尚未帐号,去注册?</a></p>
                <span class="error"></span>
{#            </form>#}
        </div>
    </div>
</div>


<script src="/static/js/jquery.js"></script>
<script type = "text/javascript">
    window.onload = function() {
            var config = {
                vx : 4,
                vy : 4,
                height : 2,
                width : 2,
                count : 100,
                color : "121, 162, 185",
                stroke : "100, 200, 180",
                dist : 6000,
                e_dist : 20000,
                max_conn : 10
            }
            CanvasParticle(config);
        }
</script>
<script type = "text/javascript" src="/static/js/canvas-particle.js"></script>
<script>
    //登陆数据
     $(".login_btn").click(function () {
         console.log(6666)
         $.ajax({
             url:"/login/",
             type:"post",
             data:{
                 user:$("#user").val(),
                 pwd:$("#pwd").val(),
                 validcode:$("#validcode").val(),
                 csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
             },
             success:function (response) {
                     {#console.log(response.user);#}
                     if(response.user){
                         //登录成功
                         console.log(111111);
                         location.href="/homepage/";
                         {#alert(111111)#}
                     }
                     else{
                        //登录失败
                         $(".error").html(response.err_msg).css("color","reg")
                       }
                     }
         })
     })
    //验证码刷新
    $("#img").click(function () {
        this.src+="?"
    })

</script>

</body>
</html>

 

# register.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style type="text/css">
        body{
             background:url(/static/img/山水.jpg);
             background-size:100%;
             background-attachment: fixed;
        }

    </style>
</head>
<body>
<h1 class="text-primary" style="text-align: center;line-height:150px">注册页面</h1>

<div class="container">
    <div class="row">
        <div class="col-lg-4 col-md-offset-4">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" class="form-control" placeholder="用户名" id="user">
                    <span class="error pull-right"></span>
                </div>
                <div class="form-group">
                    <label for="">密码</label>
                    <input type="password" class="form-control" placeholder="密码" id="pwd">
                    <span class="error pull-right"></span>
                </div>
                <div class="form-group">
                    <label for="">确认密码</label>
                    <input type="password" class="form-control" placeholder="确认密码" id="r_pwd">
                    <span class="error pull-right"></span>
                </div>
                <div class="form-group">
                    <label for="">邮箱</label>
                    <input type="email" class="form-control" placeholder="邮箱" id="email">
                    <span class="error pull-right"></span>
                </div>
                <input type="button" class="btn btn-primary btn-block pull-right" id="reg_btn" value="注册">
                <p><a href="/login/" style="text-decoration:none; text-align: center;display: block">已注册,请点击登陆</a></p>
                <span class="error pull-right"></span>
        </div>
    </div>
</div>


<script src="/static/js/jquery.js"></script>
<script type = "text/javascript">
    window.onload = function() {
            var config = {
                vx : 4,
                vy : 4,
                height : 2,
                width : 2,
                count : 100,
                color : "121, 162, 185",
                stroke : "100, 200, 180",
                dist : 6000,
                e_dist : 20000,
                max_conn : 10
            }
            CanvasParticle(config);
        }
</script>
<script type = "text/javascript" src="/static/js/canvas-particle.js"></script>
<script>
    //注册数据
    $("#reg_btn").click(function () {
        console.log(1111);
        $.ajax({
            url:"/reg/",
            type:"post",
            data:{
                csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(),
                user:$("#user").val(),
                pwd: $("#pwd").val(),
                r_pwd:$("#r_pwd").val(),
                email:$("#email").val(),
            },
            success:function (response) {
                console.log(response);
                if (response.user){
                    //注册成功
                    location.href="/login/"
                }
                else{
                    //清除错误
                    $(".error").html("");
                    $(".form-group").removeClass("has-error");

                    //展现错误
                    $.each(response.err_msg,function (i,j) {
                       $("#"+i).next().html(j[0]).css("color","red").parent().addClass("has-error")

                    })
                }

            }
        })
    })

</script>
</body>
</html>

 

# models.py

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    tel = models.CharField(max_length=32)

 

 

知识点补充总结

一、用户认证组中 用户表须要扩展时

用户认证组件中验证的信息是存储信息是依赖于 auth_user 表中的,可是该表的中字段是有限的,好比注册的时候想输入手机号等,所以,想要扩展user表,咱们就须要建立一个新的userinfo表来代替auth_user 表。javascript

须要在 setting 中设置(项目为app01,models表为UserInfo):css

AUTH_USER_MODEL="app01.UserInfo"

models中建立的表须要继承 AbstractUser,其实 auth_user  也是继承了该类,所以,新建立的表就有了原来表中的字段还能够添加新字段html

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):   #继承
    tel = models.CharField(max_length=32)

数据库的迁移:java

python manage.py makemigrations
python manage.py migrate 

#在数据库auth_user或其替表明中,建立数据(经过终端)建立超级用户 createsuperuser

二、img标签中发送请求(有个特殊功能)

img标签有一个特殊的功能,去发送请求,能够实现图片的局部刷新,典型的是验证码图片的刷新,python

加问号,等于屡次访问该地址,而验证码中该地址每次访问都会获取不一样的随机验证码图片,所以能够实现局部的刷新验证码jquery

{# 验证码的刷新 #}
        $("#valid_img_btn").click(function () {
            this.src += "?"
        });

三、验证码的校验

 登陆验证的时候,如何校验验证码?git

后台中在生成验证码的方法 获取的随机字符串 为 keep_str , 可是在login方法中,接收的数据如何跟 其余方法中的 keep_str 进行比较?ajax

有人说,将keep_str 存成全局变量 gloabl, 这样是能够知足比较,可是不一样的用户要有不一样的验证码,这样的话,只有最后用户刷新页面生成的验证码才是真的,有用户1在登陆界面,尚未来得及输入,其余用户2也进入登陆界面,那么验证码发生了变化,用户1 根据页面展现的验证码即使输入正确也会登陆失败。数据库

所以,咱们要将各用户的验证码存到各自的 session 中: django

# 设置session
request.session[keep_str":keep_str]

# 获取session
request.session.get("keep_str")

session  或者  用户认证组件,都会在 django-session表中生成记录,

一个浏览器访问会有一条记录,两个浏览器访问会有2条记录,里面的记录个数与访问的浏览器的个数一致。

session 不只能够用于登陆,校验验证码,之后还会有不少的用处。

四、JsonResponse

视图函数中,引入JsonResponse,用次返回数据,没必要再转换成就送数据,而前台接收到数据,也不须要反序列化,这些步骤在内部已经封装好了。

from django.http import JsonResponse

return JsonResponse({"user":user,"error":""})

 

五、字母与ASCII的对应

a ~ z      97 ~ 122

A ~ Z     65 ~ 90

六、Form组件中  errors中的 __all__

在全局的钩子中,其错误直接 raise ValidationError("用户密码不一致"),会存储在errors的__all__中

form = UserForm(request.POST)

if form.is_valid():
  form.cleaned_data

else:
  form.errors    #是个字典
  form.errors.get("__all__")   #全局钩子中的具体错误

可是全局钩子若是是如下,全局的错误会存储在r_pwd中:

相关文章
相关标签/搜索