BBS项目-登录功能

1 登录页面搭建

登录页面,需要输入用户名、密码、验证码,这里不用form标签,我们自己手写3个input标签。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    {% load static %}
    <script src="{% static 'jQuery-3.6.0.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
    <title>Title</title>
</head>
<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h1 class="text-center">登录</h1>
                <div class="form-group">
                    <label for="id_username">用户名</label>
                    <input type="text" name="username" id="id_username" class="form-control">
                </div>
                <div class="form-group">
                    <label for="id_pwd">密码</label>
                    <input type="password" name="password" id="id_pwd" class="form-control">
                </div>
                <div class="form-group">
                    <label for="">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="code" id="id_code" class="form-control">
                        </div>
                        <div class="col-md-6">
                            <img src="{% static 'img/default.png' %}" alt="" width="420px" height="35px">
                        </div>
                    </div>
                </div>
                <input type="button" class="btn btn-success" value="登录">
            </div>
        </div>
    </div>
</body>
</html>

先用默认图片占位,页面展示效果如下:

 

2 图片验证码

我们要做图片验证码,即图片和验证码都是动态展示的。

img标签的src属性的三种方式:1、图片文件路径    2、url    3、图片的二进制数据。

图片文件路径是固定的,我们想要动态展示,需要把src属性设置为url(路由),每当访问登录页面时,朝该url发送get请求,由后端接口动态返回二进制数据展示,login.html

<div class="col-md-6">
    <img src="/get_code/" alt="" width="430px" height="35px">
</div>

views.py

推导步骤1:直接获取后端现成的图片二进制数据发送给前端

def get_code(request):
    with open(r'static/img/222.jpg', 'rb') as f:
        data = f.read()
    return HttpResponse(data)

后端get_code接口返回图片的二进制数据,显然读取文件的方式存在问题,因为每次访问,验证码图片都是动态变化的,本地文件是有限的。

2.1 图片相关模块

只要涉及到操作图片,一般都会用到pillow模块。

pip3 install pillow
from PIL import Image, ImageDraw, ImageFont

# Image:生成图片对象
# ImageDraw:生成画笔对象,能够在图片上乱涂乱画
# ImageFont:生成字体对象,控制字体样式

推导步骤2:利用Image生成图片,第一个参数固定模式、第二个参数是前端设置的图片长宽、第三个参数是颜色,颜色可以放入三色参数(123,23,23),利用random模块随机放入三色参数,实现图片颜色动态变化。

from PIL import Image, ImageDraw, ImageFont
import random

def get_color():
    # 返回多个数据,如果没有指定类型,默认按元组形式组织数据
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

def get_code(request):
    # img_obj = Image.new('RGB', (430, 35), 'red')
    # img_obj = Image.new('RGB', (430, 35), (25, 25, 25))
    img_obj = Image.new('RGB', (430, 35), get_color())
    with open('xxx.png', 'wb') as f:   # 生成的图片先保存在本地,save方法写数据(文件句柄,图片格式)
        img_obj.save(f, 'png')
    with open('xxx.png', 'rb') as f:   # 再读取图片二进制数据,返回到前端
        data = f.read()
    return HttpResponse(data)

文件存储繁琐,IO操作频繁,效率低,需要借助于内存管理器模块。

2.2 内存管理器模块

推导步骤3:利用文件管理器对象帮我们临时存取数据。

from io import BytesIO, StringIO

# BytesIO:临时帮你存储数据 返回的时候数据是二进制
# StringIO:临时帮你存储数据 返回的时候数据是字符串

def get_code(request):
    img_obj = Image.new('RGB', (430, 35), get_color())
    io_obj = BytesIO()                      # 生成一个内存管理器对象,可以看成是文件句柄
    img_obj.save(io_obj, 'png')             # 类似于img_obj.save(f, 'png'), 第二个参数是图片类型
    return HttpResponse(io_obj.getvalue())  # 直接从内存管理器中读取二进制的图片数据,返回给前端

2.3 最终步骤 

通过前3步推导实现了图片的动态展示,我们还需要在图片上动态显示验证码,即要在图片上写字。

from PIL import Image, ImageDraw, ImageFont
from io import BytesIO, StringIO
import random


def get_code(request):
    img_obj = Image.new('RGB', (430, 35), get_color())        # 生成图片对象
    img_draw = ImageDraw.Draw(img_obj)                        # 生成画笔对象
    img_font = ImageFont.truetype('static/font/222.ttf', 30)  # 生成字体对象,第一个参数是字体样式,第二个参数是字体大小

    # 随机验证码:5位数的随机验证码,包含数字、大小写字母
    code = ''
    for i in range(5):
        random_upper = chr(random.randint(65, 90))   # 随机大写字母
        random_lower = chr(random.randint(97, 122))  # 随机小写字母
        random_inter = str(random.randint(0, 9))     # 0-9随机数字,要转成字符串形式
        tmp = random.choice([random_upper, random_lower, random_inter])   # 每循环一次,从上面三个元素中随机选择一个
        """
        将产生的随机字符串写在图片上,为什么一个个写而不是5个生成好了之后再写?因为一个个写能够控制每个字体的间隙
        第一个参数是字在图片上的坐标xy,x轴控制字的间隙,每循环一次,随i而等距离变化,y轴固定不变
        第二个参数是循环出来的字符,第三个参数是图片颜色,第四个参数是字体样式
        """
        img_draw.text((i*60+60, 0), tmp, get_color(), img_font)
        code += tmp  # 拼接随机字符串,for循环完后,就获取到5位数随机验证码code
    request.session['code'] = code  # 随机验证码要提交给登录的视图函数后端进行比对,所以要找地方存起来,并且其他视图函数也能拿到
    io_obj = BytesIO()              # 利用内存管理器帮你存储数据并返回给前端
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())

登录页面效果如下:

2.4 局部刷新验证码图片

如果验证码看不清需要更换,但是点击刷新网页是整体刷新,我们要实现局部刷新,即点击验证码图片换一张,但整体页面不刷新。

我们打开浏览器调试,找到img标签,src属性是获取验证码的url,在这个url后随便加一个东西,url变了,它会朝新的url发get请求,图片就会自动刷新。

 因此,我们给img标签绑定一个点击事件,每次点击,给src属性后加一个?

<div class="col-md-6">
    <img src="/get_code/" alt="" width="450px" height="35px" id="id_img">
    <span class="pull-right">图片看不清,点击换一张</span>
</div>

<script>
    $('#id_img').click(function (){
    let oldVal = $(this).attr('src');     // 拿到之前src的url,再给它重新赋值,用字符串拼接
    $(this).attr('src', oldVal += '?');   // 每点击图片,就会给img标签src的url后面加一个?
})
</script>

 

3 ajax提交post请求

在登录标签下加一个span标签,所有的错误信息在span标签展示。

<input type="button" class="btn btn-success" value="登录" id="id_button">
<span style="color: red" id="id_error"></span>

给button按钮绑定点击事件,提交post请求。 csrf校验用的js配置,因此不需要在data中传入 'csrfmiddlewaretoken':'{{ csrf_token }}'

 $('#id_button').click(function (){
            $.ajax({
                url: '',
                type: 'POST',
                data: {
                    'username': $('#id_username').val(),
                    'password': $('#id_pwd').val(),
                    'code': $('#id_code').val()
                },
                success: function(args){
                    if (args.code === 0){
                        window.location.href = args.url
                    }else {
                        $('#id_error').text(args.msg)
                    }
                }
            })
        })

 

4 后端登录接口

views.py

def login(request):
    if request.method == 'POST':
        back_dic = {'code': 0, 'msg': ''}
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        # 校验验证码 忽略大小写
        if code.upper() == request.session.get('code').upper():
            # 用auth组件校验用户名与密码
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                auth.login(request, user_obj)
                back_dic['url'] = '/home/'
            else:
                back_dic['code'] = 1
                back_dic['msg'] = '用户名或密码错误'
        else:
            back_dic['code'] = 1
            back_dic['msg'] = '验证码错误'
        return JsonResponse(back_dic)
    return render(request, 'login.html')

 

posted @   不会钓鱼的猫  阅读(58)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示