Django登录注册完整代码(图片、邮箱验证、加密)

前言

登录注册逻辑大多情况都雷同,每次重写一遍效率底下,为提高效率在这里放一份,有意者自取

实现的功能:

  • 图片验证码校验
  • 邮箱验证码校验
  • 错误提交后显示错误信息并保留已填写数据
  • 刷新图片验证码保留已填写数据
  • 二次确认密码校验
  • 注册用户名重复验证
  • 密码加密存储
  • 密码校验

效果图:

正文

models.py

python 复制代码
class Admin(models.Model):
    """ 用户 """
    email_validator = RegexValidator(
        regex=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
        message='请输入有效的邮箱地址'
    )
    email = models.EmailField(validators=[email_validator])
    username = models.CharField(verbose_name="用户名", max_length=128)
    password = models.CharField(verbose_name="密码", max_length=128)
    def __str__(self):
        return self.username

BootStrap.py

python 复制代码
from django import forms

class BootStrap:
    exclude_fields = []
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for name,field in self.fields.items():
            if name in self.exclude_fields:
                continue
            if field.widget.attrs:
                field.widget.attrs['class'] = 'form-control'
                field.widget.attrs['placeholder'] = '请输入' + field.label
            else:
                field.widget.attrs = {'class':'form-control','placeholder':'请输入' + field.label}

class BootStrapForm(BootStrap,forms.Form):
    pass

class BootStrapModelForm(BootStrap,forms.ModelForm):
    pass

form.py

python 复制代码
from app01.utils import BootStrap

class AdminModel(BootStrap.BootStrapModelForm):
    code = forms.CharField(label="验证码")
    class Meta:
        model = models.Admin
        fields = ['username','password']
        widgets = {
            "password":forms.PasswordInput(render_value=True)
        }

class AdminModelConfirm(BootStrap.BootStrapModelForm):
    code = forms.CharField(label="验证码")
    confirm_password = forms.CharField(label="确认密码",widget=forms.PasswordInput(render_value=True),)
    class Meta:
        model = models.Admin
        fields = "__all__"
        widgets = {
            "password":forms.PasswordInput(render_value=True)
        }
    def clean_password(self):
        pwd = make_password(self.cleaned_data['password'])
        return pwd
    def clean_confirm_password(self):
        if not check_password(self.cleaned_data['confirm_password'],self.cleaned_data["password"]):
            raise ValidationError("两次输入密码不一致")
        return make_password(self.cleaned_data['confirm_password'])
    def clean_username(self):
        user_name = self.cleaned_data['username']
        if models.Admin.objects.filter(username=user_name).exists():
            raise ValidationError("该用户名已存在")
        return user_name

code.py(生成图形验证码)

python 复制代码
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter


def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随机字母
        :return:
        """
        # return str(random.randint(0, 9))
        return chr(random.randint(65, 90))


    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)

settings.py(邮箱配置)

python 复制代码
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'  # 或其他邮件服务商
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'xxxxx@qq.com'  # 发件人邮箱
EMAIL_HOST_PASSWORD = 'xxxxxxx'  # 邮箱授权码,不是密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

相关视图函数

python 复制代码
from django.shortcuts import render, HttpResponse, redirect
from app01.utils.form import AdminModel, AdminModelConfirm
from app01.utils.code import check_code
from app01 import models
from io import BytesIO
import random
import string
from django.conf import settings
from django.http import JsonResponse
from django.contrib.auth.hashers import check_password
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags

def login(request):
    """ 处理登录逻辑 """
    if request.method == "GET":
        form = AdminModel()
        return render(request, 'login.html', {"form": form})
    form = AdminModel(request.POST)
    if form.is_valid():
        user_code = form.cleaned_data.pop('code')
        code = request.session.get('code_string', '')
        if code.upper() != user_code.upper():
            form.add_error("code", "验证码错误")
            return render(request, 'login.html', {"form": form})
        username = form.cleaned_data['username']
        password = form.cleaned_data['password']
        admin_object = models.Admin.objects.filter(username=username).first()
        if admin_object and check_password(password, admin_object.password):
            request.session.pop('code_string')
            request.session['info'] = {"id": admin_object.id, "username": admin_object.username}
            request.session.set_expiry(60 * 60 * 24 * 7)
            return redirect("/order/list/")
        form.add_error("username", "用户名或密码错误")
        return render(request, 'login.html', {"form": form})
    return render(request, 'login.html', {"form": form})

def image_code(request):
    """ 生成图形验证码 """
    img, code_string = check_code()
    request.session['code_string'] = code_string
    request.session.set_expiry(60)
    stream = BytesIO()
    img.save(stream, 'png')
    return HttpResponse(stream.getvalue())

def register(request):
    """ 处理注册逻辑 """
    if request.method == "GET":
        form = AdminModelConfirm()
        return render(request, 'register.html', {"form": form})
    form = AdminModelConfirm(request.POST)
    if form.is_valid():
        user_code = form.cleaned_data.pop('code')
        code = request.session.get('email_code', '')
        if code != user_code:
            form.add_error("code", "验证码错误或过期")
            return render(request, 'register.html', {"form": form})
        form.save()
        return redirect("/login/")
    return render(request, 'register.html', {"form": form})

def email_code(request):
    """ 发送邮箱验证码 """
    email = request.GET.get('to_email')
    captcha = "".join(random.sample(string.digits, k=4))
    content = {
        "email_code": captcha
    }
    html_content = render_to_string('email.html', content)
    text_content = strip_tags(html_content)
    email_object = EmailMultiAlternatives(
        subject="您的验证码",
        body=text_content,
        from_email=f"xxx.com <{settings.DEFAULT_FROM_EMAIL}>",
        to=[email],
    )
    email_object.attach_alternative(html_content, 'text/html')
    result = email_object.send()
    if result:
        request.session["email_code"] = captcha
        request.session.set_expiry(120)
        return JsonResponse({"status": True})
    return JsonResponse({"status": True})

urls.py(相关路由配置)

python 复制代码
from django.urls import path
from app01.views import login

urlpatterns = [  
    path("login/",login.login),
    path("image/code/",login.image_code),
    path("register/",login.register),
    path("email/code/",login.email_code),
]

下面的html中会导入bootstrap和jQuery,可以自行网上寻找对应版本,不想自己找的话我这里放一份,里面都有

链接: https://pan.baidu.com/s/12dDtwFhbkuSSJfzAnLXDhQ?pwd=0916 提取码: 0916

login.html

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    <script src="{% static '/plugins/jQuery/jquery-3.7.1.js' %}"></script>
    <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
    <style>
        .account {
            width: 400px;
            border: 1px solid #dddddd;
            border-radius: 5px;
            box-shadow: 5px 5px 20px #aaa;

            margin-left: auto;
            margin-right: auto;
            margin-top: 100px;
            padding: 20px 40px;
        }
        .account h2 {
            margin-top: 10px;
            text-align: center;
        }
    </style>
</head>
<body>
<div class="account">
    <h2>用户登录</h2>
    <form method="post" novalidate id="loginForm">
        {% csrf_token %}
        <div class="form-group">
            <label>用户名</label>
            {{ form.username }}
            <span style="color: red;">{{ form.username.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label>密码</label>
            {{ form.password }}
            <span style="color: red;">{{ form.password.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label for="id_code">图片验证码</label>
            <div class="row">
                <div class="col-xs-7">
                    {{ form.code }}
                    <span style="color: red;">{{ form.code.errors.0 }}</span>
                </div>
                <div class="col-xs-5">
                    <img id="image_code" src="/image/code/" style="width: 125px;">
                    <a href="javascript:void(0);" onclick="refresh()" style="position: absolute">看不清?点击刷新</a>
                </div>
            </div>
        </div>
        <input type="submit" value="登 录" class="btn btn-primary">
        <a href="/register/" style="margin-left: 20px">没有账户?点击注册</a>
    </form>
</div>
<script type="text/javascript">
    function refresh(){
        sessionStorage.setItem('username',$("#id_username").val())
        sessionStorage.setItem('password',$("#id_password").val())
        location.reload()
    }
    $(function(){
        var username = sessionStorage['username']
        var password = sessionStorage['password']

        if (username){
            $("#id_username").val(username)
            sessionStorage.removeItem('username')
        }
        if (password){
            $("#id_password").val(password)
            sessionStorage.removeItem('password')
        }

    })
</script>
</body>
</html>

register.html

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <link rel="stylesheet" href="{% static '/plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
    <script src="{% static '/plugins/jQuery/jquery-3.7.1.js' %}"></script>
    <script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
    <style>
        .account {
            width: 400px;
            border: 1px solid #dddddd;
            border-radius: 5px;
            box-shadow: 5px 5px 20px #aaa;

            margin-left: auto;
            margin-right: auto;
            margin-top: 100px;
            padding: 20px 40px;
        }

        .account h2 {
            margin-top: 10px;
            text-align: center;
        }


        .btn-countdown {
            background-color: #337ab7;
            color: white;
            transition: all 0.3s;
        }

        .btn-countdown:hover:not(:disabled) {
            background-color: #286090;
        }

        .btn-countdown:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
    </style>
</head>
<body>
<div class="account">
    <h2>用户注册</h2>
    <form method="post" novalidate>
        {% csrf_token %}
        <div class="form-group">
            <label>邮箱号</label>
            {{ form.email }}
            <span style="color: red;">{{ form.email.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label>用户名</label>
            {{ form.username }}
            <span style="color: red;">{{ form.username.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label>密码</label>
            {{ form.password }}
            <span style="color: red;">{{ form.password.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label>确认密码</label>
            {{ form.confirm_password }}
            <span style="color: red;">{{ form.confirm_password.errors.0 }}</span>
        </div>
        <div class="form-group">
            <label for="id_email">邮箱验证码</label>
            <div class="row">
                <div class="col-xs-7">
                    <input class="form-control" name="code" placeholder="请输入验证码">
                    <span style="color: red;">{{ form.code.errors.0 }}</span>
                </div>
                <div class="col-xs-5">
                    <button type="button" class="btn btn-default btn-countdown" id="id_emailCode">发送验证码</button>
                </div>
            </div>
        </div>
        <input type="submit" value="注 册" class="btn btn-primary">
    </form>
</div>
<script type="text/javascript">
    $(document).ready(function () {
        function startCountdown(seconds) {
            let countdown = seconds;
            const $button = $("#id_emailCode");
            const originalText = $button.text();

            $button.prop("disabled", true);

            const countdownInterval = setInterval(function () {
                $button.text(`重新发送(${countdown}秒)`);
                countdown--;

                if (countdown < 0) {
                    clearInterval(countdownInterval);
                    $button.text(originalText);
                    $button.prop("disabled", false);
                }
            }, 1000);
        }


        $("#id_emailCode").click(function () {
            $.ajax({
                url: "/email/code/",
                type: "GET",
                data: {
                    to_email: $("#id_email").val()
                },
                dataType: "json",
                success: function (req) {
                    if (req.status) {
                        startCountdown(120);
                    }else{
                        alert("发送失败,请检查或稍后重试")
                    }
                }
            });
        });
    });
</script>

</body>
</html>
相关推荐
陈天伟教授2 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
2301_764441333 小时前
Aella Science Dataset Explorer 部署教程笔记
笔记·python·全文检索
爱笑的眼睛113 小时前
GraphQL:从数据查询到应用架构的范式演进
java·人工智能·python·ai
BoBoZz193 小时前
ExtractSelection 选择和提取数据集中的特定点,以及如何反转该选择
python·vtk·图形渲染·图形处理
liwulin05063 小时前
【PYTHON-YOLOV8N】如何自定义数据集
开发语言·python·yolo
信看3 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai3 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
爱吃大芒果3 小时前
Flutter 主题与深色模式:全局样式统一与动态切换
开发语言·javascript·flutter·ecmascript·gitcode
木头左3 小时前
LSTM量化交易策略中时间序列预测的关键输入参数分析与Python实现
人工智能·python·lstm