Django实现的登录注册功能

news2024/7/6 20:48:50

1 前言

在Web开发中,用户登录和注册是最基本且必不可少的功能。Django,作为一个高级的Python Web框架,为我们提供了强大的工具和库来快速实现这些功能。下面,我将详细介绍如何使用Django来实现用户登录和注册功能。

2 功能介绍

该项目是使用django+bootstrp开发的项目,包含以下功能

  • 注册: 手机获取验证码、ModelForm数据验证、验证码redis超时处理,
  • 登录:手机验证码登录、账号密码登录、生成随机图片验证码、用户信息seesion处理

项目示例

1 账号密码登录
在这里插入图片描述
2 短信验证码登录
在这里插入图片描述
3 用户的注册
在这里插入图片描述
4 用户退出
在这里插入图片描述

3 申请容联云短信服务

登录和注册都需要一个短信的验证码,但是阿里、腾讯的短信服务申请太麻烦了,所有就申请了容联云短信服务,新用户有8元的免费额度只要项目不正式上线本地测试的话申请还是没有什么问题的,

容联云,全球智能通讯云服务商 (yuntongxun.com)

3.1 创建应用

在这里插入图片描述

3.2 创建短信模板

在这里插入图片描述

3.3 填写测试号码

在这里插入图片描述

3.4 安装sdk

 pip install ronglian_sms_sdk

3.5 发送短信的python代码

import json
from ronglian_sms_sdk import SmsSDK

# 容联云创建的应用中获取
accId = "应用的id"
accToken = "应用的token"
appId = "appid"


def send_sms(mobile, sms_code):
    """发送短信"""
    sdk = SmsSDK(accId, accToken, appId)
    tid = "1"
    try:
        resp = sdk.sendMessage(tid, mobile, (sms_code, ))
        res_json = json.loads(resp)
        if res_json.get("statusCode") == "000000":
            logger.info(f"向mobile[{mobile}]发送短信验证码[{sms_code}]成功")
        else:
            logger.info(f"发送短信失败:{resp}")
    except Exception as e:
        logger.error(f"发送短信失败:{e}")



if __name__ == '__main__':
    send_sms("平台测试手机号码", "9991")

运行脚本后手机会受到短信验证码,如下图
在这里插入图片描述

4 注册

在这里插入图片描述

注册分为两个部分:

  • 点击获取验证码:前端通过ajax发送请求到后端,后端生成随机验证码随机保存到redis中,同时调用短信服务发送短信到用户手机。
  • 点击注册:用户输入用户信息和验证码之后,后端进行校验,没有问题时保存到数据库,同时跳转到登录页面。
    在这里插入图片描述
    代码实现: 直截关键代码,完整代码跳转最后下载完整代码
# setting.py  django 使用redis
# 安装
pip install django-redis
# setting
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://192.168.1.200:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100, "encoding": "utf-8"}
            # "PASSWORD": "123",
        }
    }
}

from django_redis import get_redis_connection
def test_django_redis(request):
    # 可以直接从连接池中拿到连接
    redis=get_redis_connection()

    age = str(conn.get('age'), encoding='utf-8')
    # 设置过期时间
    redis.set('name','xiaoming',4)  
    redis.set('xxx',test_redis)
# view.py
from django.shortcuts import render
from django.http.response import JsonResponse
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
from django_redis import get_redis_connection
from users import models


class RegisterModelForm(forms.ModelForm):
    mobile_phone = forms.CharField(label="手机号",
                                   validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ])
    password = forms.CharField(label="密码",
                               min_length=8,
                               max_length=64,
                               error_messages={
                                   'min_length': "密码长度不能小于8个字符",
                                   'max_length': "密码长度不能大于64个字符"
                               },
                               widget=forms.PasswordInput())
    confirm_password = forms.CharField(label="重复密码",
                                       min_length=8,
                                       max_length=64,
                                       error_messages={
                                           'min_length': "重复密码长度不能小于8个字符",
                                           'max_length': "重复密码长度不能大于64个字符"
                                       },
                                       widget=forms.PasswordInput())
    code = forms.CharField(label="验证码", widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        # fields = "__all__"
        fields = ["username", "email", "password", "confirm_password", "mobile_phone", "code"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    def clean_confirm_password(self):
        pwd = self.cleaned_data.get('password')
        confirm_pwd = self.cleaned_data['confirm_password']
        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已注册')
        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']

        # mobile_phone = self.cleaned_data['mobile_phone']

        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误,请重新输入')

        return code


def register(request):
    """用户注册视图"""
    if request.method == "GET":
        form = RegisterModelForm()
        return render(request, "users/register.html", {"form": form})
    elif request.method == "POST":
        form = RegisterModelForm(data=request.POST)
        if form.is_valid():
            form.save()
            logger.info(f"用户[{form.username}]注册成功")
            return JsonResponse({"status": True})
        logger.warning(f"用户[{form.username}]注册失败")
        return JsonResponse({"status": False, "error": form.errors})

主要是用Django的ModeForm对数据格式进行验证,以及注册时候的一些逻辑判断

5 短信验证码登录

在这里插入图片描述
短信验证码登录也分为两个部分

  • 点击获取验证码: 这个过程和用户的注册获取验证码类似,可以复用注册获取手机验证的代码
  • 点击登录: 输入手机号码和验证码之后,后端校验数据的有效性,将用户的信息保存在session中,跳转主页
    在这里插入图片描述
    代码实现: 直截关键代码,完整代码跳转最后下载完整代码
# view,py 中数据校验和逻辑处理
class LoginSMSForm(forms.Form):
    """短信验证码登录模板"""
    mobile_phone = forms.CharField(label='手机号',
                                   validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
                                   )
    code = forms.CharField(label='验证码',
                           widget=forms.TextInput())

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if not exists:
            raise ValidationError('用户未注册')
        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        redis = get_redis_connection()
        redis_code = redis.get(mobile_phone)  # 根据手机号去获取验证码
        if not redis_code:
            raise ValidationError('验证码失效或未发送,请重新发送')
        real_code = redis_code.decode('utf-8')
        if code.strip() != real_code:
            raise ValidationError('验证码错误,请重新输入')
        return code


def login_sms(request):
    """短信验证码登录"""
    if request.method == "GET":
        form = LoginSMSForm()
        return render(request, "users/login_sms.html", context={"form": form})
    elif request.method == "POST":
        form = LoginSMSForm(data=request.POST)
        if form.is_valid():
            logger.info(f"用户登录成功")
            return JsonResponse({"status": True})
        logger.warning(f"用户登录失败")
        return JsonResponse({"status": False, "error": form.errors})

主要是用Django的ModeForm对数据格式进行验证,以及登录时候的一些逻辑判断

6 用户密码登录

在这里插入图片描述
用户密码登录也分为两个部分

  • 生成图像验证码: 加载网页时会请求后端生成图像验证码的接口,后端会根据pillow画出一张图片返回给前端,同时将验证码保存到session中, 图像验证码具体怎么生成的看生成随机图片验证码-CSDN博客
  • 点击登录: 填好信息后点击登录,请求端口登录接口后端会对数据进行验证,成功后跳转。
    在这里插入图片描述

代码实现: 直截关键代码,完整代码跳转最后下载完整代码

class LoginForm(forms.Form):
    username = forms.CharField(label='用户名',
                               min_length=4,
                               max_length=64,
                               error_messages={
                                   'min_length': "密码长度不能小于4个字符",
                                   'max_length': "密码长度不能大于64个字符"
                               })
    password = forms.CharField(label="密码",
                               min_length=8,
                               max_length=64,
                               error_messages={
                                   'min_length': "密码长度不能小于8个字符",
                                   'max_length': "密码长度不能大于64个字符"
                               },
                               widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='图片验证码',
                           widget=forms.TextInput())

    def __init__(self, request,  *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = '请输入%s' % (field.label,)

    def clean_username(self):
        username = self.cleaned_data["username"]
        if not models.UserInfo.objects.filter(username=username).exists():
            logger.warning("用户未注册")
            raise ValidationError("用户未注册")
        return username

    def clean_password(self):
        username = self.cleaned_data.get("username")
        if not username:
            logger.warning("用户名为空")
            return username
        password = self.cleaned_data['password']
        encrypt_password = md5(password)
        if not models.UserInfo.objects.filter(username=username, password=encrypt_password).exists():
            logger.warning("用户名或者密码错误")
            raise ValidationError("用户名或者密码错误")
        return encrypt_password

    def clean_code(self):
        code = self.cleaned_data["code"]
        session_code = self.request.session.get("image_code")
        if not session_code:
            logger.warning("验证码已过期, 请求重新获取")
            raise ValidationError("验证码已过期, 请求重新获取")
        if code.strip().upper() != session_code.strip().upper():
            logger.warning(f"验证码输入错误,{code}:{session_code}")
            raise ValidationError("验证码输入错误")
        return code


def login(request):
    """用户账号密码登录"""
    if request.method == "GET":
        form = LoginForm(request)
    else:
        form = LoginForm(request, data=request.POST)
        if form.is_valid():
            username = form.cleaned_data["username"]
            user_obj = models.UserInfo.objects.filter(username=username).first()
            request.session["user_id"] = user_obj.id
            request.session.set_expiry(settings.SESSION_EXPIRY)
            logger.info("用户登录成功")
            return redirect("home")
        logger.warning("用户登录失败")
    return render(request, "users/login.html", context={"form": form})

主要是用Django的ModeForm对数据格式进行验证,以及登录时候的一些逻辑判断

6 运行项目

完整代码一下链接下载

【免费】Django+bootstrp实现用户的注册和登录功能资源-CSDN文库

# 1 下载代码解压
# 2 安装依赖
pip install -r requirement.txt
# 3 迁移数据库
python manage.py makemigrations
python manage.py migrate
# 4 登录容联云获取accId、accToken、appId替换sms.py
# 5 在setting中修改redis中的地址
# 4 启动项目
python manage.py runserver

另外安装redis的教程也可以看::Docket常见的软件部署1-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1581703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Ollama在本地运行AI大模型gemma

1.下载: https://github.com/ollama/ollama/releases 2.配置环境变量 我的电脑-右键-属性-系统-高级系统设置-环境变量-【系统环境变量】新建 变量名:OLLAMA_MODELS (固定变量名) 变量值:E:\Ollama\Lib &#xff0…

【状态机dp】【 排序 】 2809使数组和小于等于 x 的最少时间

本文涉及知识点 【状态机dp】 排序 LeetCode 2809. 使数组和小于等于 x 的最少时间 给你两个长度相等下标从 0 开始的整数数组 nums1 和 nums2 。每一秒&#xff0c;对于所有下标 0 < i < nums1.length &#xff0c;nums1[i] 的值都增加 nums2[i] 。操作 完成后 &…

Vue.js高效前端开发(页面验证跳转,查)

效果图 代码&#xff1a; new.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><div id"app"><input type"text" name"" id"&…

SecureCRT通过私钥连接跳板机,再连接到目标服务器(图文教程)

文章目录 1. 配置第一个session&#xff08;跳板机&#xff09;2. 设置本地端口3. 设置全局firewall4. 配置第二个session&#xff08;目标服务器&#xff09; 服务器那边给了一个私钥&#xff0c;现在需要通过私钥连接跳板机&#xff0c;再连接到目标服务器上 &#x1f349; …

Softing WireXpert 4500线缆认证仪的独特之处——双控系统

Softing推出的WireXpert 4500线缆认证仪&#xff0c;可用于结构化布线认证&#xff0c;认证速率高达40Gb/s。该线缆认证仪专为实用性而设计&#xff0c;重量轻&#xff0c;手感舒适&#xff0c;主机与副机均配备6英寸工业LCD触摸屏&#xff0c;使其更适用于布线现场。 WireXper…

4.9总结

多线程 程序内部的执行路径就叫线程&#xff0c;为了满足需求同时执行多个任务&#xff0c;就有了多线程 多线程有两种应用方式&#xff1a; 第一种是并发&#xff1a;在同一时刻&#xff0c;有多个指令在单个CPU上交替运行 第二种是并行&#xff1a;在同一时刻&#xff0c…

网络网络层之(7)PPPOE协议

网络网络层之(7)PPPOE协议 Author: Once Day Date: 2024年4月7日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day…

搜维尔科技:Patchwork 3D工业仿真实时渲染,将CAD 数据转换成真实感的3D模型以用于工业用途

Patchwork 3D工业仿真 实时渲染点击跳转官网 从实时渲染到真实照片 根据工作阶段所需的逼真度&#xff0c;您可以使用三个渲染引擎&#xff0c;从最快的&#xff08;OpenGL&#xff0c;交互式&#xff09;到最逼真的&#xff08;光线跟踪&#xff0c;Iray物理逼真&#xff09;…

数据仓库的概念和作用?如何搭建数据仓库?

随着企业规模的扩大和数据量的爆炸性增长&#xff0c;有效管理和分析海量数据成为企业数字化转型的关键。而在互联网的普及过程中&#xff0c;信息技术已深入渗透各行业&#xff0c;逐渐融入企业的日常运营。然而&#xff0c;企业在信息化建设中面临了一系列困境和挑战&#xf…

皮灵顿靶机1

1 先在kali上扫描靶机ip地址 .1、.2、.254、.138都是kali自身的一些ip地址&#xff0c;所以在此确认要进行攻击的靶机的ip地址为&#xff1a;192.168.84.132 再查看端口&#xff0c;一个22端口一个80端口 我这里使用dirb&#xff0c;查看网站的信息 最特别的是robots.txt …

python如何去掉换行符

使用strip()函数去掉每行结束的\n。 strip()函数 声明&#xff1a;str为字符串&#xff0c;chars为要删除的字符序列。 str.strip(chars&#xff09;&#xff1a;删除s字符串中开头、结尾处&#xff0c;位于chars删除序列的字符。 str.lstrip(chars)&#xff1a;删除s字符串…

vue中使用axios获取不到响应头Content-Disposition的解决办法

项目中&#xff0c;后端返回的文件流; 前端需要拿到响应头里的Content-Disposition字段的值&#xff0c;从中获取文件名 在控制台Headers中可以看到相关的字段和文件名&#xff0c;但是在axios里面却获取不到 如果想要让客户端访问到相关信息&#xff0c;服务器不仅要在head…

maxpool long数据类型报错

报错&#xff1a; RuntimeError: “max_pool2d” not implemented for ‘Long’ 源码&#xff1a; import torch from torch import nn from torch.nn import MaxPool2dinput torch.tensor([[1, 2, 0, 3, 1],[0, 1, 2, 3, 1],[1, 2, 1, 0, 0],[5, 2, 3, 1, 1],[2, 1, 0, 1, 1…

python爬虫-----爬虫解析—xpath(第十八天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

JVM—垃圾收集器

JVM—垃圾收集器 什么是垃圾 没有被引用的对象就是垃圾。 怎么找到垃圾 引用计数法 当对象引用消失&#xff0c;对象就称为垃圾。 对象消失一个引用&#xff0c;计数减去一&#xff0c;当引用都消失了&#xff0c;计数就会变为0.此时这个对象就会变成垃圾。 在堆内存中主…

环境监测站升级选择ARM网关驱动精准数据采集

物联网技术的深入发展和环保需求的不断攀升&#xff0c;API调用网关在环境监测领域的应用正成为科技创新的重要推手。其中&#xff0c;集成了API调用功能的ARM工控机/网关&#xff0c;以其出色的计算性能、节能特性及高度稳定性&#xff0c;成功搭建起连接物理世界与数字世界的…

MySQL分库分表的方式有哪些

目录 一、为什么要分库分表 二、什么是分库分表 三、分库分表的几种方式 1.垂直拆分 2. 水平拆分 四、分库分表带来的问题 五、分库分表技术如何选型 一、为什么要分库分表 如果一个网站业务快速发展&#xff0c;那这个网站流量也会增加&#xff0c;数据的压力也会随之而…

3D应用模型信创系统实时渲染有什么要求?

实时云渲染技术是数字孪生领域&#xff0c;比较常用的轻量化软件交付方式&#xff0c;该技术是将3D应用等大模型的算力执行放在了服务器端&#xff0c;而服务器目前比较常用的还是Windows系统。但随着国产信创在数字孪生领域应用越来越多&#xff0c;实时云渲染平台的国产信创化…

LabVIEW和2D激光扫描的受电弓滑板磨耗精确测量

LabVIEW和2D激光扫描的受电弓滑板磨耗精确测量 在电气化铁路运输中&#xff0c;受电弓滑板的健康状况对于保障列车安全行驶至关重要。受电弓滑板作为连接电网与列车的直接介质&#xff0c;其磨损情况直接影响到电能的有效传输及列车的稳定运行。精确、快速测量受电弓滑板磨损情…

在【Cencos7】中安装【Nacos】并适配【PostgreSQL】数据库

在【Cencos7】中安装【Nacos-2.3.0】并适配【PostgreSQL】数据库 安装JDK wget命令下载&#xff1a; wget https://repo.huaweicloud.com/java/jdk/8u151-b12/jdk-8u151-linux-x64.tar.gz解压 tar -xzvf jdk-7u80-linux-x64.tar.gz将解压后的目录移动到/opt下 sudo mv jdk…