Django使用图片验证码加邮箱或手机号登录

63 篇文章 0 订阅
53 篇文章 0 订阅

实现页面效果

在这里插入图片描述

实现思路

  • 使用form渲染数据
  • 校验手机号(格式、是否注册)、密码以及验证码

生成图片验证码

'''
pillow:是python处理图片的模块,很强大
'''
import random

from PIL import Image, ImageDraw, ImageFont


def picture_code():
    def get_char():
        '''通过数字获取ASCII表中的对应字母'''
        return chr(random.randint(65, 90))

    def get_color():
        '''获取随机颜色'''
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    # mode:色系  size:宽高 color:颜色
    # RGB颜色对照表:https://tool.oschina.net/commons?type=3
    img = Image.new(mode='RGB', size=(120, 50), color=(220, 220, 220))

    # 创建画笔对象
    draw = ImageDraw.Draw(img, mode='RGB')

    # 画点 xy:基于图片的坐标,fill:颜色
    for i in range(10):
        draw.point([random.randint(0, 120), random.randint(0, 50)], fill='green')

    # 画线 xy:(起点坐标,终点坐标),width:粗细
    for i in range(5):
        draw.line(
            [random.randint(0, 120), random.randint(0, 50), random.randint(0, 120), random.randint(0, 50)],
            fill='red'
        )

    # 画圆或弧线 start and end:角度
    for i in range(3):
        x = random.randint(0, 120)
        y = random.randint(0, 50)
        x2 = x + 4
        y2 = y + 4
        draw.arc([x, y, x2, y2], start=0, end=90, fill='red')

    # 注意点:.ttf文件路径不能含有中文
    font = ImageFont.truetype('fonts/domi.ttf', 35)
    char_list = []
    for i in range(5):
        char = get_char()
        char_list.append(char)
        height = random.randint(10, 15)
        draw.text([18 * (i + 1), height], text=char, fill='red', font=font)

    char_code = ''.join(char_list)

    return img, char_code


if __name__ == '__main__':
    img, r = picture_code()
    # 创建在内存中,这里直接生成在本地,方便自己调式查看
    with open('code.png', 'wb') as f:
        img.save(f, format='png')  # format:格式

    print(r)

在页面上展示验证码

首先后端需要先定义一个生成验证码的路由和视图。

path('img_code/', account.img_code, name='img_code'),
def img_code(request):
    img, char_code = picture_code()
    # 将图片数据写到内存
    stream = BytesIO()
    img.save(stream, format='png')

    # 将验证码放到session中 也可以放到redis中
    request.session['image_code'] = char_code

    # 图片验证码有效期,单位秒
    request.session.set_expiry(settings.IMAGE_CODE_EXPIRY)

    return HttpResponse(stream.getvalue())

怎么在前端页面中展示验证码:

 <img id="img_code" height="34" width="120" src="{% url "img_code" %}" alt="">

完整页面代码:

<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <div class="account">
                <h2 class="text-center">用户登录</h2>
                <form id="form_data">
                    {% csrf_token %}
                    {% for field in form %}
                        {% if field.label == '验证码' %}
                            <div class="form-group">
                                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                                <div class="row">
                                    <div class="col-xs-6">
                                        {{ field }}
                                        <span class="error_msg"></span>
                                    </div>
                                    <div class="col-xs-6">
                                        <img id="img_code" height="34" width="120" src="{% url "img_code" %}" alt="">
                                    </div>
                                </div>
                            </div>
                        {% else %}
                            <div class="form-group">
                                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                                {{ field }}
                                <span class="error_msg"></span>
                            </div>
                        {% endif %}
                    {% endfor %}
                <div>
                     <button type="button" id="login" class="btn btn-primary">登 录</button>
                    <div class="pull-right">
                        <a href="{% url 'login_sms' %}">短信验证码登录>>></a>
                    </div>
                </div>
                </form>
            </div>
        </div>
    </div>
</div>

发送登录数据和点击刷新验证码:

// 发送登录数据
    function sendLoginData() {
        // 获取用户输入的所有数据
        // 将数据发送到后端
        // 根据响应结果进行一些页面效果处理

        // 绑定点击事件
        $('#login').click(function () {
            var data = $('#form_data').serialize(); //拿到form表单中的所有数据
            $.ajax({
                url: '{% url 'login' %}',
                type: 'post',
                data: data,
                success:function (res) {
                    if (res.status){
                        location.href = res.path;
                    }else{
                        $.each(res.error_msg,function (k,v){
                            $('#id_' + k).next().text(v)
                        })
                    }
                }
            })
        })
    }

    // 点击刷新图片验证码的事件
    function refreshImageCode() {
        $('#img_code').click(function () {
            var oldSrc = $(this).attr('src');
            $(this).attr('src',oldSrc + '?'); // 问号会重新发送一次请求
        })
    }

views.py:

def login(request):
    if request.method == 'GET':
        form = LoginForm(request)
        return render(request, 'user/login.html', dict(form=form))
    else:
        form = LoginForm(request, data=request.POST)
        if form.is_valid():
            return JsonResponse(dict(status=True, path=reverse('index')))
        else:
            return JsonResponse(dict(status=False, error_msg=form.errors))

form校验类:

from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Q

from web import models
from web.utils.bootstrap_style import BootStrapForm
from web.utils.enc import set_md5


class LoginForm(BootStrapForm, forms.Form):
    '''手机号或者邮箱登录'''
    username = forms.CharField(label='手机号或者邮箱')
    password = forms.CharField(widget=forms.PasswordInput, label='密码')
    image_code = forms.CharField(label='验证码')

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        '''密码加密传输'''
        password = self.cleaned_data.get('password')
        return set_md5(password)

    def clean_image_code(self):
        '''验证图片验证码
        * 用户输入的和后台保存的要一致
        '''
        user_code = self.cleaned_data.get('image_code', '')
        session_code = self.request.session.get('image_code', '')
        if not session_code:
            raise ValidationError('验证码已经过期!!')

        if user_code.strip().upper() != session_code.upper():
            raise ValidationError('验证码错误!!!')

        return session_code

    def clean(self):
        '''手机号邮箱校验'''
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        uname_or_email = models.UserInfo.objects.filter(
            Q(Q(phone=username)| Q(email=username)),
            password=password
        ).exists()
        if not uname_or_email:
            self.add_error('username','用户名或密码错误!!')

        return self.cleaned_data
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值