【三】登录功能实现

【一】登录功能

【路由接口】

  • 登陆主接口

    • login/
  • 获取验证码接口

    • get_code/

【二】需求

  • 需要展示的效果
    • 用户输入用户名
    • 用户输入密码
    • 用户输入验证码
  • 要求
    • 用户名、密码不对时,实时展示提示信息,及必要报错
    • 展示动态图像验证码
      • 验证码内容构成为 5 位 字母+数字
  • 使用到的技术
    • bootstrap展示样式
    • ajax加载点击动作
    • ajax发送post请求

【三】功能实现 - 前端页面搭建输入数据

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

       <!--  CDN 链接 引入方法  -->
    <!--  Bootstrap 的 CSS 样式文件  -->
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
    <!--  Bootstrap 的 JS 文件 (动画效果需要jQuery)  -->
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js">// bootstrap </script>
    <!--  jQuery 文件  -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"> // jquery</script>
    <!--  以下为 css样式书写区  -->
    <style>


    </style>

</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="username">用户名</label>
                <input type="text" name="username" id="username" class="form-control">
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" name="password" id="password" 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="/get_code/" alt="" height="38" width='310' id="id_code_img">
                    </div>
                </div>
            </div>
            <input type="button" class="btn btn-success" value="登陆" id="id_commit">
            <span style="color:red;" id="error"></span>
        </div>
    </div>
</div>

<script>
    // 更新验证码图片
    $("#id_code_img").click(function () {
        // 首先获取标签之前的src
        let oldVal = $(this).attr("src");
        $(this).attr("src", oldVal += '?')
    })

    // 绑定按钮事件,提交post请求
    $("#id_commit").click(function () {
        $.ajax({
            url: "",
            type: "post",
            data: {
                "username": $('#username').val(),
                "password": $('#password').val(),
                "code": $('#id_code').val(),
                // 模版语法
                "csrfmiddlewaretoken": '{{ csrf_token }}',
            },
            success: function (args) {
                if (args.code === 1000) {
                    // 跳转到首页
                    window.location.href = args.url
                } else {
                    // 渲染错误信息
                    $("#error").text(args.message)
                }
            }
        })
    })

</script>

</body>
</html>

【1】前端页面搭建

  • 首先是页面搭建

    • 页面需要具有的功能
      • 输入用户名
      • 输入密码
      • 输入验证码
<h1 class="text-center">登录功能</h1>
<div class="form-group">
    <label for="username">用户名</label>
    <input type="text" name="username" id="username" class="form-control">
</div>
<div class="form-group">
    <label for="password">密码</label>
    <input type="password" name="password" id="password" class="form-control">
</div>
  • 动态验证码加载
    • 定义一个输入框,用来给用户输入验证码
    • 定义一个图片标签,实时展示更新的图片
      • tips:
        • src属性:src="/get_code/"
        • 在后面跟任意字符可自动刷新请求
<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="/get_code/" alt="" height="38" width='310' id="id_code_img">
        </div>
    </div>
</div>

【2】前端页面事件绑定

(1)点击验证码图片更新验证码

// 更新验证码图片
$("#id_code_img").click(function () {
    // 首先获取标签之前的src
    let oldVal = $(this).attr("src");
    $(this).attr("src", oldVal += '?')
})
  • 找到展示图片的图片标签
    • $("#id_code_img").click(function () {})
  • 绑定点击事件
    • 获取标签
      • let oldVal = $(this).attr("src");
    • 绑定属性
      • $(this).attr("src", oldVal += '?')
    • 每次点击按钮触发事件,在路由尾部添加任意字符,二次访问后端的生成图片验证码接口,返回验证码

(2)给按钮绑定点击事件,发送Ajax请求

// 绑定按钮事件,提交post请求
$("#id_commit").click(function () {
    $.ajax({
        url: "",
        type: "post",
        data: {
            "username": $('#username').val(),
            "password": $('#password').val(),
            "code": $('#id_code').val(),
            // 模版语法
            "csrfmiddlewaretoken": '{{ csrf_token }}',
        },
        success: function (args) {
            if (args.code === 1000) {
                // 跳转到首页
                window.location.href = args.url
            } else {
                // 渲染错误信息
                $("#error").text(args.message)
            }
        }
    })
})
  • 找到按钮并绑定事件

    • $("#id_commit").click(function () {})
  • 绑定Ajax事件

    • $.ajax({})
  • 向当前url发送数据

    • url: ""
  • 请求方式是post

    • type: "post"
  • 需要传的数据

    • 通过id值拿到输入框输入的值

      • $('#username').val()
    • 通过id值拿到输入框输入的值

      • "password": $('#password').val()
    • 通过id值拿到输入框输入的值

      • $('#id_code').val()
    • 通过模板语法拿到 csrf 校验值

      • '{{ csrf_token }}'
    • 构建数据格式

      data: {
                  "username": $('#username').val(),
                  "password": $('#password').val(),
                  "code": $('#id_code').val(),
                  // 模版语法
                  "csrfmiddlewaretoken": '{{ csrf_token }}',
              },
      
    • 请求成功或失败,打印提示信息

      success: function (args) {
                      if (args.code === 1000) {
                          // 跳转到首页
                          window.location.href = args.url
                      } else {
                          // 渲染错误信息
                          $("#error").text(args.message)
                      }
                  }
      

【四】功能实现 - 后端校验数据返回错误信息

【1】返回图片验证码

# 图片相关的模块
#  pip install pillow
from PIL import Image, ImageDraw, ImageFont

'''
Image       生成图片
ImageDraw   能够在图片上乱涂乱画
ImageFont   控制字体样式
'''
import random
# 内存管理器模块
from io import BytesIO, StringIO

'''
BytesIO     临时存储数据,返回的数据是二进制数据
StringIO    临时存储数据,返回的数据是字符串数据
'''

# 随机获取 0-255数字
def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


# 获取随机验证码
def get_code(request, *args, **kwargs):

    # 方法四:写图片验证码
    img_obj = Image.new('RGB', (310, 38), get_random())
    # 产生一个画笔对象
    img_draw = ImageDraw.Draw(img_obj)
    # (字体样式,字体大小)
    img_font = ImageFont.truetype('book/static/font/汉仪秀英体简.ttf', 30)

    # 随机验证码 -- 五位数(数字/小写字母/大写字母)
    code = ''
    for i in range(5):
        random_int = str(random.randint(0, 9))  # 0-9之间的整数
        random_upper = chr(random.randint(65, 90))  # A-Z之间的字母
        random_lower = chr(random.randint(97, 122))  # a-z之间的字母
        temp = random.choice([random_int, random_upper, random_lower])
        '''
        一个个写而不是一次写多个
        因为一个个写能够控制每个字体的间隙,生成多个一起写没办法控制间隙
        '''
        img_draw.text((i * 60 + 20, 4), temp, get_random(), img_font)
        # 拼接随机字符串
        code += temp
    print(code)
    # 随机验证码在登录的视图函数中需要比对,所以需要找地方存起来,便于其他函数调用
    request.session['code'] = code
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    data = io_obj.getvalue()
    return HttpResponse(data)

【2】登陆校验逻辑

# 登陆接口
def login(request, *args, **kwargs):
    if request.method == 'POST':
        back_dict = {"code": 1000, "message": ""}
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        # (1) 校验验证码是否一致
        if request.session.get('code').upper() == code.upper():
            # (2)校验用户名和密码是否一致
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # (3) 保存用户状态
                auth.login(request, user_obj)
                back_dict["url"] = "/home/"
            else:
                back_dict['code'] = 2000
                back_dict["message"] = "用户名或密码错误"
        else:
            back_dict['code'] = 2000
            back_dict["message"] = "验证码错误"
        return JsonResponse(back_dict)
    return render(request, 'login.html', locals())
  • 视图主逻辑

    • 校验当前请求方式,限制数据请求输入方式

      • if request.method == 'POST':
    • 构建基础返回数据字典格式

      • back_dict = {"code": 1000, "message": ""}
    • 取到前端输入的用户名/密码/验证码

      • username = request.POST.get('username')
      • password = request.POST.get('password')
      • code = request.POST.get('code')
    • 校验验证码是否一致(可选不区分大小写/区分大小写)

      • if request.session.get('code').upper() == code.upper():
    • 校验验证码失败

      • 更改状态码
      • back_dict['code'] = 2000
      • 更改提示信息
      • back_dict["message"] = "验证码错误"
    • 校验验证码成功往下走

    • 校验两次密码是否一致

      • 利用auth模块自带的校验方法

        • auth.authenticate 是一个 Django 框架中的函数,用于对用户进行身份验证。
          • 在Django中,身份验证是通过检查用户提供的凭据(例如用户名和密码)来确认用户的身份。
        • 当你的应用程序涉及到需要验证用户身份的操作时
          • 你可以使用 auth.authenticate 函数来验证用户是否为有效用户。
          • 该函数接收两个参数:request 对象和可选的 usernamepassword 参数。
        • auth.authenticate
          • 在验证成功时返回用户对象
          • 如果验证失败则返回 None
        • 这个函数主要用于登录过程以及其他需要验证身份的场景。
          • 一旦用户被认证,你可以将其存储在会话中、分配权限或进行其他相关操作。
        • 下面是一个使用 auth.authenticate 的示例:
        from django.contrib import auth
        
        def login(request):
            if request.method == 'POST':
                username = request.POST['username']
                password = request.POST['password']
                
                user = auth.authenticate(request, username=username, password=password)
                
                if user is not None:
                    # 身份验证成功
                    auth.login(request, user)
                    # 执行其他操作
                else:
                    # 身份验证失败
                    # 执行其他操作或返回错误信息
        
        • 以上的代码演示了从请求中获取用户名和密码,并使用 auth.authenticate 对其进行身份验证。
          • 如果验证成功,则可以执行其他操作;
          • 否则,你可以返回错误消息或执行其他相应的操作。
        • 需要注意的是,auth.authenticate 函数
          • 只是验证用户的身份
          • 而不会创建会话或进行其他跟踪操作。
        • 要完整地实现用户登录功能,通常还需要使用 auth.login 函数来创建会话并将用户标记为已登录状态。
      • user_obj = auth.authenticate(request, username=username, password=password)

    • 校验成功则保存用户状态

      • 使用auth模块自带的方法
      • auth.login(request, user_obj)
      • 成功后,定义下一个跳转页面,跳转到首页
      • back_dict["url"] = "/home/"
    • 校验失败

      • 更改状态码
      • back_dict['code'] = 2000
      • 更改报错信息
      • back_dict["message"] = "用户名或密码错误"
    • 将信息返回至前端页面展示

      • return JsonResponse(back_dict)
  • 返回登录页面,及逻辑函数名称空间

    • return render(request, 'login.html', locals())
posted @ 2023-07-21 15:42  Chimengmeng  阅读(31)  评论(0编辑  收藏  举报
/* */