Django实现登录注册(带验证码)


前言

话不多说,先上效果图:

正文

Django的安装什么的咱都不说,直接进入正题,我这里使用的Django==3.0.7。新建Django项目也很简单,在Pycharm里面New Project--->Django,然后设置一下项目名称基本就可以了,如下图:

项目建好之后要简单配置一下,我习惯先把时区和语言改了,然后设置静态文件的路径(css, js, img等),都在settings.py里编辑,静态文件的路径根据自己的实际路径来,我习惯放在根目录下。

LANGUAGE_CODE = 'zh-hans'  # 中文支持

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False  # 默认是 True,时间是utc时间,由于我们要用本地时间,所用手动修改为false
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

HTML文件放在templates目录下,一般这个目录都自动建好了,没有的自己手动建一个,我这里是直接在根目录下的。

1)新建app——users

manage.py startapp  users

运行上述命令,创建成功的目录如下(图中的myforms.py是我后来自己创建的,这个后面会用到):

要实现用户登录和注册功能,需要保存用户的相关信息。很显然,我们至少需要一个用户表User,我这里需要在用户表里保存下面的信息:

  • 用户名
  • 密码
  • 邮箱地址
  • 创建时间

我们先修改models.py文件,在该文件建立用户的模型,然后迁移到数据库即可。修改后的models.py如下:

from datetime import datetime

from django.db import models


class User(models.Model):
    """
    用户信息
    """

    username = models.CharField('账号', max_length=16, unique=True)
    password = models.CharField('密码', max_length=16)
    email = models.EmailField('邮箱', unique=True)
    c_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['c_time']  # 元数据里定义用户按创建时间的反序排列,也就是最近的最先显示
        verbose_name = '用户'
        verbose_name_plural = '用户'

    def __str__(self):  # 使用__str__帮助人性化显示对象信息
        return self.username

在迁移数据库之前,我们要先配置数据库,编辑settings.py,找到DATABASES,我这里用的是默认的数据库,默认的配置如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

如果需要用到MySQL,则配置如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',        #数据库名字
        'USER': 'root',          #账号
        'PASSWORD': '123456',      #密码
        'HOST': '127.0.0.1',    #IP
        'PORT': '3306',                   #端口
    }
}

数据库配置完成后,接着在settings.py中注册app:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
]

运行如下命令迁移到数据库:

python manage.py makemigrations
 
python manage.py migrate

 

在users的admin.py中注册模型:

from django.contrib import admin
from . import models
# Register your models here.

admin.site.register(models.User)

这个时候登录后台管理员账户,应该可以看到多了一个表,Django是自带管理后台的,我们只需注册一个超级管理员即可。

manmage.py createsuperuser

2)图形验证码

为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能。

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。

图形验证码在后面配置路由、表单等多处需要,这里我们先介绍。图形验证码只需要安装一个库即可——django--simple-captcha,怎么安装很简单不再赘述,安装完成后在settings.py中注册一下就可以了,如下:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'captcha',
]

具体用法在后面讲述,这里只是先声明一下。

3)路由设置

我们这里采用二级路由,具体看代码:

  • 在项目的urls.py中
urlpatterns = [
    path('user/', include('users.urls')),  # 函数 include() 允许引用其它 URLconfs
    path('admin/', admin.site.urls),
    path('captcha', include('captcha.urls'))
]
  • 在users的urls.py中
urlpatterns = [
    path('admin', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('register/', views.register),
    path('logout/', views.logout),
]

4)编写视图

这里我就整个直接贴出代码:

from django.shortcuts import render, redirect
from . import models
from users.myforms import UserForm, RegisterForm
import hashlib


def hash_code(s, salt='Covid-19'):  # 加点盐
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())  # update方法只接收bytes类型
    return h.hexdigest()


def index(request):
    pass
    return render(request, 'index.html')


def login(request):
    if request.session.get('is_login', None):  # 防止重复登录
        return redirect('/index')

    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "请检查填写的内容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(username=username)
                if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对
                    request.session['is_login'] = True  # 往session字典内写入用户状态和数据
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.username
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户不存在!"
        return render(request, 'login.html', locals())
    login_form = UserForm()
    return render(request, 'login.html', locals())


def register(request):
    if request.session.get('is_login', None):  # 登录状态不允许注册
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        if register_form.is_valid():  # 获取数据
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            if password1 != password2:  # 判断两次密码是否相同
                message = "两次输入的密码不同!"
                return render(request, 'register.html', locals())
            else:
                same_name_user = models.User.objects.filter(username=username)
                if same_name_user:  # 用户名唯一
                    message = '用户名已经存在!'
                    return render(request, 'register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:  # 邮箱地址唯一
                    message = '该邮箱地址已被注册!'
                    return render(request, 'register.html', locals())

                # 当一切都OK的情况下,创建新用户

                new_user = models.User.objects.create()
                new_user.username = username
                new_user.password = hash_code(password1)  # 使用加密密码
                new_user.email = email
                new_user.save()
                return redirect('/user/login/')  # 自动跳转到登录页面
    register_form = RegisterForm()
    return render(request, 'register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):  # 如果本来就未登录,也就没有登出一说
        return redirect("/index/")
    request.session.flush()  # 将session中的所有内容全部清空
    return redirect('/index/')

hash_code函数将密码由明文转为密文,我这里采用python自带的hashlib库实现。在上面的代码中重点讲一下session和locals(),表单放到后面讲。

当session启用后,传递给视图request参数的HTTPRequest对象将包含一个session属性,就像一个字典对象一样,我们可以在Django的任何地方读写request.sesssion属性,或者多次编辑使用它。

Python内置了一个locals()函数,它返回当前所有的本地变量字典,我们可以偷懒将这作为render函数的数据字典参数值,就不用费劲去构造一个形如{'message':message, 'login_form':login_form} 的字典了。这样做的好处就是大大的方便了我们,但是同时也可能往模板传入了一些多余的变量数据,造成了数据冗余降低效率。

return render(request, 'login.html', {'message':message, 'login_form':login_form}
 
大约等同于
 
return render(request, 'login.html', locals())

5)Django表单

myforms.py

from captcha.fields import CaptchaField, CaptchaTextInput
from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="账号", max_length=16, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label="密码", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='验证码', widget=CaptchaTextInput(attrs={'class': 'form-control'}))


class RegisterForm(forms.Form):
    username = forms.CharField(label="账号", max_length=16, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密码", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="确认密码", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='验证码', widget=CaptchaTextInput(attrs={'class': 'form-control'}))

说明:

  1. 要先导入forms模板
  2. 所有的表单类都要继承forms.Form类
  3. 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中<form>内的一个input元素。这一点和Django模板系统的设计非常相似。
  4. label参数用于设置<label>标签
  5. max_length 限制字段输入的最大长度。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过字符数,二是在后端服务器验证用户输入的长度也不可超过。
  6. widget = forms.PasswordInput 用于指定该字段在form表单里表现为 <input type='password'> ,也就是密码输入框。

Django的表单很重要的一个功能就是自动生成HTML的form表单内容。直接{{ login_form }} 虽然好,啥都不用操心,但是界面真的不尽人意,往往不是我们想要的,在form类里添加Attr属性即可。

6)HTML文件

login.html

    <div class="col-md-4 col-sm-6 border p-0 offset-md-4 offset-sm-6 my-5">
        <h3 class="text-center bg-info p-2 text-white mb-0">登录</h3>
        <form class='p-5 bg-white' action="/user/login/" method="post">
            {% if message %}
                <div class="alert alert-warning">{{ message }}</div>
            {% endif %}
            {% csrf_token %}
            <div class="form-group">
                {{ login_form.username.label_tag }}
                {{ login_form.username }}
            </div>
            <div class="form-group">
                {{ login_form.password.label_tag }}
                {{ login_form.password }}
            </div>
            <div class="form-group">
                {{ login_form.captcha.errors }}
                {{ login_form.captcha.label_tag }}
                {{ login_form.captcha }}
            </div>
            <div class="pt-3 pb-5">
                <button type="submit" class="btn btn-primary pull-left mb-5">立即登录</button>
                <a class="btn btn-danger pull-right mb-5" href="/user/register/">前往注册</a>
            </div>
        </form>
    </div>

这里额外的添加了一条 {{ login_form.captcha.errors }} 用于明确指示用户,你的验证码不正确。其中验证图形码是否正确的工作都是在后台自动完成的。只需要使用is_valid()这个myforms内置的验证方法就一起进行了,完全不需要再视图函数中添加任何的验证代码,非常方便快捷!

register.html

    <div class="col-md-4 col-sm-6 border p-0 offset-md-4 offset-sm-6 my-5">
        <h3 class="text-center bg-info p-2 text-white mb-0">注册</h3>
        <form class='px-5 py-3 bg-white' action="/user/register/" method="post">
            {% if message %}
                <div class="alert alert-warning">{{ message }}</div>
            {% endif %}
            {% csrf_token %}
            <div class="form-group">
                {{ register_form.username.label_tag }}
                {{ register_form.username }}
            </div>
            <div class="form-group">
                {{ register_form.password1.label_tag }}
                {{ register_form.password1 }}
            </div>
            <div class="form-group">
                {{ register_form.password2.label_tag }}
                {{ register_form.password2 }}
            </div>
            <div class="form-group">
                {{ register_form.email.label_tag }}
                {{ register_form.email }}
            </div>
            <div class="form-group">
                {{ register_form.captcha.errors }}
                {{ register_form.captcha.label_tag }}
                {{ register_form.captcha }}
            </div>
            <div class="pt-3">
                <p class="text-center m-0"><button type="submit" class="btn btn-primary btn-block mb-5">立即注册</button></p>
            </div>
        </form>
    </div>

前端只展示了一部分关键代码,采用了bootstrap框架,下面这段js代码是用来实现图形验证码的切换的(django-simple-captcha没有提供这个功能):

    <script>
        $(function () {
            // Add refresh button after field (this can be done in the template as well)
            $('img.captcha').after(
                $('<a class="btn btn-sm text-white btn-info pull-right captcha-refresh">看不清</a>')
            );

            $('.captcha-refresh').click(function () {
                var $form = $(this).parents('form');
                var url = location.protocol + "//" + window.location.hostname + ":"
                    + location.port + "/captcha/refresh/";

                // Make the AJAX-call
                $.getJSON(url, {}, function (json) {
                    $form.find('input[name="captcha_0"]').val(json.key);
                    $form.find('img.captcha').attr('src', json.image_url);
                });
                return false;
            });
        });
    </script>

参考博客:https://www.cnblogs.com/wj-1314/p/10751411.html

项目GitHub地址:https://github.com/Marvin-wen/CovidCTImagesDetection


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM