登录功能前端验证码

img标签的src属性可以放的值:

  1. 本地图片的地址
  2. 图片的二进制数据
  3. 后端url, 页面渲染完毕后会自动朝该url发送get请求

验证2和3

'''
views.py
def get_code(request):
    with open('avatar/111.jpg', 'rb') as f:
        data = f.read()
    return HttpResponse(data)
    
login.html
<img src="/get_code/" alt="" width="360" height="35">
'''

存取随机生成的图片

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

'''
pillow模块(生成图片):
    Image: 生成图片
    ImageDraw: 在图片上写字
    ImageFont: 控制字的字体样式
    
io模块(内存管理):
    BytesIO: 保存数据, 并且在获取时是以二进制形式获取
    StringIO: 保存数据, 并且在获取时是以字符串形式获取
'''


def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


def get_code(request):
    # 方式一: 以文件形式存取, 有io时间, 较慢
    # img_obj = Image.new('RGB', (360, 35), get_random())  # 生成图片对象
    # with open(r'avatar/xxx.png', 'wb') as f:  # 需先将图片对象保存到本地
    #     img_obj.save(f, 'png')
    # with open(r'avatar/xxx.png', 'rb') as f:  # 将保存的随机生成的图片数据读取
    #     data = f.read()
    # return HttpResponse(data)  # 将保存的随机生成的图片数据发送
    
    # 方式二: 通过io模块在内存中存取在内存中存取, 较快
    img_obj = Image.new('RGB', (360, 35), get_random())  # 生成图片对象
    img_draw = ImageDraw.Draw(img_obj)  # 生成画笔对象
    img_font = ImageFont.truetype('static/font/444.TTF', 30)  # 控制字体样式的文件以.ttf结尾
    
    code = ''
    for i in range(5):
        upper_str = chr(random.randint(65, 90))  # 将数字转换成ascii码对应的字符
        lower_str = chr(random.randint(97, 122))
        digit_str = chr(random.randint(0, 9))
        temp = random.choice([upper_str, lower_str, digit_str])  # 从大写字母, 小写字母, 数字中随机选取一个
        img_draw.text((i * 60 + 60, 0), temp, get_random(), img_font)  # 在图片中写入随机选取的字符, 并控制字符间距
        code += temp  # 存储写入的字符
        
	request.session['code'] = code  # session存储验证码, 是全局的视图函数都能访问
    
    io_obj = BytesIO()  # 生成一个io对象, 相当于文件句柄
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())  # 获取二进制图片数据

验证码点击刷新及登录功能的实现

验证码点击刷新

'''
# login.html
<script>
    $('#id_img').on(
        'click',
        function () {
            let oldPath = $(this).attr('src');
            $(this).attr('src', oldPath += '?')
        }
    );

    ...
</script>
'''

登录功能实现

'''
# views.py
def login(request):
    if request.method == 'POST':
        back_dic = {'code': 1000, 'msg': ''}
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')

        if request.session.get('code').upper() == code.upper():  # 验证码忽略大小写
            user_obj = auth.authenticate(request, username=username, password=password)  # auth模块校验用户名和密码
            if user_obj:
                auth.login(request, user_obj)  # 记录用户登录状态
                back_dic['msg'] = '登录成功'
                back_dic['url'] = '/home/'  # 登录成功页面跳转到首页
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或密码错误'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码错误'
        print(back_dic)
        return JsonResponse(back_dic)
        
    return render(request, 'login.html')

# login.html
<script>
    ...

    $('#id_submit').on(
        'click',
        function () {
            $.ajax({
                url: '',
                type: 'post',
                data: {
                    'username': $('#id_username').val(),
                    'password': $('#id_password').val(),
                    'code': $('#id_code').val(),
                    'csrfmiddlewaretoken': '{{ csrf_token }}',
                },
                success: function (data) {
                    if (data.code == 1000) {
                        window.location.href = data.url;  // 前端控制页面跳转
                    }
                    else {
                        $('#id_error').text(data.msg);
                        let oldPath = $('#id_img').attr('src');  // 用户提交的信息错误时刷新验证码
                        $('#id_img').attr('src', oldPath += '?')
                    }
                }
            })
        }
    )
</script>    
'''

导航条注销和修改密码两个功能的实现

'''
# views.py
@login_required  # 全局配置未登录时的跳转页面: settings.py-->LOGIN_URL = '/login/'
def logout(request):
    auth.logout(request)  # 使用auth模块的注销功能
    _url = reverse('home')  # 反向解析
    return redirect(_url)


@login_required
def change_password(request):
    ...

        is_right = request.user.check_password(old_password)  # 使用auth模块的单独校验密码的功能
        ...

        return JsonResponse(back_dic)
        
# home.html
<button type="button" class="btn btn-primary" id="id_change_password">修改</button>

<script>
    $('#id_change_password').on(
        'click',
        function () {
            $.ajax({  // ajax提交POST请求
                url: {% url 'change_password' %},  // 反向解析
                type: 'post',
                data: {
                    ...
                },
                success: function (data) {
                    if (data.code == 1000) {
                        window.location.href = data.url;  // 前端控制页面跳转
                    }
                    else {
                        $('#id_error').text(data.error)  // 前端展示报错信息
                    }
                }
            })
        }
    );

    $('input').on(  
        'focus',
        function () {  // input框获取焦点时清除报错信息
            $('#id_error').text('')
        }
    )
</script>
'''