邮箱注册登录验证 | 模态对话框 | Django开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了邮箱注册登录验证 | 模态对话框 | Django开发相关的知识,希望对你有一定的参考价值。

1.创建注册用户表,以及验证码临时表

技术分享图片
from django.db import models


# 发送邮件临时表:验证码,邮箱,发送次数,当前发送时间
class SendMsg(models.Model):

    nid = models.AutoField(primary_key=True)
    code = models.CharField(max_length=6)
    email = models.CharField(max_length=32, db_index=True)
    times = models.IntegerField(default=0)
    ctime = models.DateTimeField()


# 模态对话框中用户表;用户名,密码,邮箱,当前注册时间;
class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32, unique=True)
    password = models.CharField(max_length=32)
    email = models.CharField(max_length=32, unique=True)
    ctime = models.DateTimeField()
View Code

2.注册

① 前端js实现点击获取验证码效果,以及将Form表单返回的错误信息显示到页面

技术分享图片
        <!-- 点击发送邮件过程中,前端点击的js动态效果,以及请求后端后返回json格式的判断错误信息 -->
        function BindSendMsg(){
            $("#fetch_code").click(function(){
                // 点击时,先将整体错误清空
                $(‘#register_error_summary‘).empty();
                // 获取邮箱输入的值
                var email = $(‘#email‘).val();
                // 没有填写邮箱,提示错误信息
                if(email.trim().length == 0){
                    // 这个位置有一个div标签,留空给错误信息
                    $(‘#register_error_summary‘).text(‘请输入注册邮箱‘);
                    return;
                }
                // 点击的按钮,添加一个sending样式,‘已发送‘
                if($(this).hasClass(‘sending‘)){
                    // 遇到return下面不再继续执行函数
                    return;
                }
                var ths = $(this);
                var time = 60;

                $.ajax({
                    url: "/send_msg/",
                    type: ‘POST‘,
                    data: {email: email},
                    dataType: ‘json‘,
                    // arg是后端返回的Json格式的字典,通过字典的值对应判断,执行操作;
                    success: function(arg){
                        // summary返回统一错误信息,error返回对应错误信息;
                        // {‘status‘: False, "summary": ‘整体错误信息‘, ‘error‘: {}}
                        if(!arg.status){
                            // 如果后端返回格式错误状态,就将整体错误写到html中;
                            $(‘#register_error_summary‘).text(arg.summary);
                        }else{
                            // 如果后端返回格式正确状态,表示后台已经发送成功,添加sending样式;
                            ths.addClass(‘sending‘);
                            var interval = setInterval(function(){
                                ths.text("已发送(" + time + ")");
                                // 时间倒计时
                                time -= 1;
                                // 当时间读到0,更新倒计时操作,去掉sending样式状态,重写‘获取验证码‘
                                if(time <= 0){
                                    // 因为每次点击事件,代码都是从上到下执行,
                                    // 所以不需要手动修改返回时间到60秒;
                                    clearInterval(interval);
                                    ths.removeClass(‘sending‘);
                                    ths.text("获取验证码");
                                }
                                // 1000毫秒,每秒执行一次这个点击事件中的匿名函数;
                            }, 1000);
                        }
                    }
                });

            });
        }
View Code

② 后端Form表单,验证邮箱格式

技术分享图片
from django import forms


# 对发送验证码时的表单验证,只验证邮箱
class SendMsgForm(forms.Form):
    email = forms.EmailField()
View Code

③ 点击发送邮箱验证码的后端操作,并做一段时间内注册的次数限制、

技术分享图片
# 随机验证码
def random_code():
    code = ‘‘
    for i in range(4):
        current = random.randrange(0,4)
        if current != i:
            temp = chr(random.randint(65,90))
        else:
            temp = random.randint(0,9)
        code += str(temp)
    return code
View Code
技术分享图片
class BaseResponse:

    def __init__(self):
        self.status = False
        self.code = StatusCodeEnum.Success
        self.data = None
        self.summary = None
        self.message = {}
View Code
技术分享图片
def send_msg(request):
    """
    注册时,发送邮箱验证码
    :param request:
    :return:
    """
    rep = BaseResponse()
    form = SendMsgForm(request.POST)
    if form.is_valid():
        # 如果验证成功,保存正确数据
        _value_dict = form.clean()
        # 取出邮箱作为验证是否已经被注册
        email = _value_dict[email]

        # 如果用户邮箱已经存在了呢?
        # 判断用户表中是否含有这个邮箱
        # 如果已经存在
        has_exists_email = models.UserInfo.objects.filter(email=email).count()

        if has_exists_email:
            rep.summary = "此邮箱已经被注册"
            return HttpResponse(json.dumps(rep.__dict__))

        # 获取当前时间
        current_date = datetime.datetime.now()
        # 发送,获取随机获取验证码
        code = commons.random_code()
        # 查看临时表中是否有当前邮箱
        count = models.SendMsg.objects.filter(email=email).count()
        # 如果没有当前邮箱,就添加邮箱
        if not count:
            models.SendMsg.objects.create(code=code, email=email, ctime=current_date)
            rep.status = True
        # 如果已经存在当前邮箱,对时间与次数做限制,一个小时只能访问10次
        else:
            # 当前发送时间前一个小时
            limit_day = current_date - datetime.timedelta(hours=1)
            # 查看当前邮箱在一个小时内是否又发送了9次
            times = models.SendMsg.objects.filter(email=email, ctime__gt=limit_day, times__gt=9).count()
            # 如果该邮箱已经超出发送次数限制,就在对象中写入错误提示信息
            if times:
                rep.summary = "‘已超最大次数(1小时后重试)‘"
            # 否则可以继续发送
            else:
                # 如果上次发送时间距离现在一小时以上就刷新访问字数
                unfreeze = models.SendMsg.objects.filter(email=email, ctime__lt=limit_day).count()
                if unfreeze:
                    models.SendMsg.objects.filter(email=email).update(times=0)

                from django.db.models import F
                # 更新验证码与当前时间,访问次数+1
                models.SendMsg.objects.filter(email=email).update(code=code,
                                                                  ctime=current_date,
                                                                  times=F(times) + 1)
                # 当前处于可以发送状态
                rep.status = True
    else:
        #error_dict = json.loads(form.errors.as_json())
        #rep.summary = error_dict[‘email‘][0][‘message‘]
        # 表单验证错误,取出错误信息
        rep.summary = form.errors[email][0]
    return HttpResponse(json.dumps(rep.__dict__))
View Code
技术分享图片
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr


def email(email_list, content, subject="标题信息"):
    msg = MIMEText(content, plain, utf-8)
    msg[From] = formataddr(["来自用户...",[email protected]])
    msg[Subject] = subject
    # SMTP服务
    server = smtplib.SMTP("[email protected]", 25) # 25表示端口
    server.login("[email protected]", "Mic")  # 用户名及密码
    server.sendmail([email protected], email_list, msg.as_string())
    server.quit()

# 什么时候执行发送邮箱?
# email([‘对方邮箱@qq.com‘, ... ], ‘邮箱发送内容‘)
View Code

④ 点击注册(Form表单验证,判断用户名,邮箱是否存在,用户表中写入注册信息)

技术分享图片
class RegisterForm(forms.Form):
    username = forms.CharField()
    email = forms.EmailField()
    password = forms.CharField()
    email_code = forms.CharField()
View Code
技术分享图片
def register(request):
    """
    注册
    :param request:
    :return:
    """
    # 创建对象,自动生成验证错误信息等普通字段
    rep = BaseResponse()
    # 创建form对象验证输入信息判断
    form = RegisterForm(request.POST)
    # 如果表单验证成功,
    if form.is_valid():
        current_date = datetime.datetime.now()
        limit_day = current_date - datetime.timedelta(minutes=1)
        # 获取表单数据
        _value_dict = form.clean()

        # 超过一分钟,如果没有验证码表示过期
        is_valid_code = models.SendMsg.objects.filter(email=_value_dict[email],
                                                      code=_value_dict[email_code],
                                                      ctime__gt=limit_day).count()
        if not is_valid_code:
            rep.message[email_code] = 邮箱验证码不正确或过期
            return HttpResponse(json.dumps(rep.__dict__))

        has_exists_email = models.UserInfo.objects.filter(email=_value_dict[email]).count()
        # 判断邮箱是否存在
        if has_exists_email:
            rep.message[email] = 邮箱已经存在
            return HttpResponse(json.dumps(rep.__dict__))
        # 判断用户名是否存在
        has_exists_username = models.UserInfo.objects.filter(username=_value_dict[username]).count()
        if has_exists_username:
            rep.message[email] = 用户名已经存在
            return HttpResponse(json.dumps(rep.__dict__))

        # ??????
        _value_dict[ctime] = current_date
        _value_dict.pop(email_code)
        # 当前用户的所有信息
        # 将注册信息写入用户表中
        obj = models.UserInfo.objects.create(**_value_dict)

        user_info_dict = {nid: obj.nid, email: obj.email, username: obj.username}
        # 删除临时表中该邮箱数据
        models.SendMsg.objects.filter(email=_value_dict[email]).delete()

        # 将注册信息写入session表中
        request.session[is_login] = True
        request.session[user_info] = user_info_dict
        rep.status = True

    # 如果表单验证失败,写入错误信息
    else:
        error_msg = form.errors.as_json()
        rep.message = json.loads(error_msg)
    return HttpResponse(json.dumps(rep.__dict__))
View Code

3.登录

① 获取验证码,写入session

技术分享图片
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
# pip3 install Pillow
_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ‘‘.join(map(str, range(3, 10)))  # 数字
init_chars = ‘‘.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    ‘‘‘
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    ‘‘‘

    width, height = size # 宽, 高
    img = Image.new(mode, size, bg_color) # 创建图形
    draw = ImageDraw.Draw(img) # 创建画笔

    def get_chars():
        ‘‘‘生成给定长度的字符串,返回列表格式‘‘‘
        return random.sample(chars, length)

    def create_lines():
        ‘‘‘绘制干扰线‘‘‘
        line_num = random.randint(*n_line) # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        ‘‘‘绘制干扰点‘‘‘
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        ‘‘‘绘制验证码字符‘‘‘
        c_chars = get_chars()
        strs =  %s  %  .join(c_chars) # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)

        return ‘‘.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)

    return img, strs
View Code
技术分享图片
def check_code(request):
    """
    获取验证码
    :param request:
    :return:
    """
    stream = io.BytesIO()
    # 创建随机字符 code
    # 创建一张图片格式的字符串,将随机字符串写到图片上
    img, code = CheckCode.create_validate_code()
    img.save(stream, "PNG")
    # 将字符串形式的验证码放在Session中
    request.session["CheckCode"] = code
    return HttpResponse(stream.getvalue())
View Code

② 表单验证

技术分享图片
class LoginForm(forms.Form):
    user = forms.CharField()
    pwd = forms.CharField()
    code = forms.CharField()
View Code

③ 登录

技术分享图片
        function SubmitLogin(ths){
            $(ths).children(‘:eq(0)‘).addClass(‘hide‘);
            $(ths).addClass(‘not-allow‘).children(‘:eq(1)‘).removeClass(‘hide‘);
            // 发送Ajax请求
            //完成之后
            $(‘#model_login .inp .error‘).remove();

            var post_dict = {};
            $(‘#model_login input‘).each(function(){
                post_dict[$(this).attr("name")] = $(this).val();
            });

            $.ajax({
                url: ‘/login/‘,
                type: ‘POST‘,
                data: post_dict,
                dataType: ‘json‘,
                success: function(arg){
                    if(arg.status){
                        window.location.href = ‘/index‘;
                    }else{
                        $.each(arg.message, function(k,v){

                            //<span class="error">s</span>
                            var tag = document.createElement(‘span‘);
                            tag.className = ‘error‘;
                            tag.innerText = v[0][‘message‘];
                            $(‘#model_login input[name="‘+ k +‘"]‘).after(tag);
                        })
                    }
                }
            });

            $(ths).removeClass(‘not-allow‘).children(‘:eq(1)‘).addClass(‘hide‘);
            $(ths).children(‘:eq(0)‘).removeClass(‘hide‘);
        }
View Code
技术分享图片
def login(request):
    """
    用户登陆
    :param request:
    :return:
    """
    rep = BaseResponse()  #  存放消息的类
    # 创建登陆的验证表单
    form = LoginForm(request.POST)
    # 如果验证成功
    if form.is_valid():
        # 获取表单正确数据
        _value_dict = form.clean()
        # 将表单验证码与存在session中单额验证码对比,如果队不上,写入错误信息
        if _value_dict[code].lower() != request.session["CheckCode"].lower():
            rep.message = {code: [{message: 验证码错误}]}
            return HttpResponse(json.dumps(rep.__dict__))
        # 如果验证码正确,验证用户名和密码
        from django.db.models import Q
        # ------------------------------------
        # models.UserInfo.objects.filter(Q(Q(username=u)&Q(pwd=p))|Q(Q(email=u)&Q(pwd=p)))
        # 无法动态实现循环操作
        # ------------------------------------
        con = Q()

        q1 = Q()
        q1.connector = AND
        q1.children.append((email, _value_dict[user]))
        q1.children.append((password, _value_dict[pwd]))
        # 相当于Q(username=u)&Q(pwd=p)

        q2 = Q()
        q2.connector = AND
        q2.children.append((username, _value_dict[user]))
        q2.children.append((password, _value_dict[pwd]))

        # 将q1,q2包裹到一个大Q里;
        con.add(q1, OR)
        con.add(q2, OR)

        obj = models.UserInfo.objects.filter(con).first()
        # 如果没有同时满足登陆的条件就写入提示信息
        if not obj:
            rep.message = {user: [{message: 用户名邮箱或密码错误}]}
            return HttpResponse(json.dumps(rep.__dict__))

        # 登入成功,将填写的登录信息写入session中
        request.session[is_login] = True
        request.session[user_info] = {nid: obj.nid, email: obj.email, username: obj.username}
        rep.status = True
    # 如果表单验证失败,写入错误信息
    else:
        error_msg = form.errors.as_json()
        rep.message = json.loads(error_msg)

    return HttpResponse(json.dumps(rep.__dict__))
View Code

④ 注销登录(清除session)

技术分享图片
def logout(request):
    """
    用户注销
    :param request:
    :return:
    """
    # 用户注销,清除session
    request.session.clear()
    return redirect(/index/)
View Code

 4.模态对话框HTML页面

技术分享图片
    <link rel="stylesheet" href="/statics/css/commons.css" />
    <link rel="stylesheet" href="/statics/plugins/tab-menu-box/tab.css" />
View Code
技术分享图片
    <div id="accountDialog" class="account-dialog hide clearfix">
        <div id="model_login" class="login left">
            <div class="header">登陆</div>
            <div class="content">
                <div style="padding: 0 70px">
                    <div class="tips">
                        <span>用户名登陆</span>
                        <span style="padding: 0 5px;">|</span>
                        <span>邮箱登陆</span>
                    </div>
                    <div id="login_error_summary" class="error-msg">

                    </div>
                    <div class="inp">
                        <input name="user" type="text" placeholder="请输入用户名或邮箱" />
                    </div>
                    <div class="inp">
                        <input name="pwd" type="password" placeholder="请输入密码" />
                    </div>
                    <div class="inp clearfix">
                        <input name="code" class="check-code" type="text" placeholder="请输入验证码" />
                        <span>
                            <img class="check-img" src="/check_code/" alt="验证码" onclick="ChangeCode(this);">
                        </span>

                    </div>
                    <div class="extra">
                        <input type="checkbox" name="autoLogin" checked="checked" /> <span>一个月内自动登录</span>
                        <a class="right" href="javascript:void(0);">忘记密码?</a>
                    </div>
                    <div class="inp">
                        <div class="submit" onclick="SubmitLogin(this);">
                            <span>登陆</span>
                            <span class="hide">
                                <img src="/statics/images/loader.gif" style="height: 16px;width: 16px">
                                <span>正在登陆</span>
                            </span>
                        </div>
                    </div>
                </div>
                <script>
                    function ChangeCode(ths) {
                        ths.src += ?;
                    }
                </script>
            </div>
        </div>
        <div id="model_register" class="register left">
            <div class="header">
                <span>注册</span>
                <div class="dialog-close" onclick="CloseDialog(‘#accountDialog‘);">X</div>
            </div>
            <div class="content">
                <div style="padding: 0 70px">
                    <div class="tips">
                        <span>输入注册信息</span>
                    </div>
                    <div id="register_error_summary" class="error-msg">

                    </div>
                    <div class="inp">
                        <input name="username" type="text" placeholder="请输入用户名" />

                    </div>
                    <div class="inp">
                        <input name="email" id="email" type="text" placeholder="请输入邮箱" />
                    </div>
                    <div class="inp">
                        <input name="email_code" class="email-code" type="text" placeholder="请输入邮箱验证码" />
                        <a id="fetch_code" class="fetch-code" href="javascript:void(0);">获取验证码</a>
                    </div>
                    <div class="inp">
                        <input name="password" type="password" placeholder="请输入密码" />
                    </div>
                    <div class="inp">
                        <div class="submit" onclick="SubmitRegister(this);">
                            <span>注册</span>
                            <span class="hide">
                                <img src="/statics/images/loader.gif" style="height: 16px;width: 16px">
                                <span>正在注册</span>
                            </span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
View Code

 5.前端JS函数调用

技术分享图片
    <script type="text/javascript">
        $(function () {
            BindTabMenu(‘#tab-menu-title‘, ‘#tab-menu-body‘);

            BindLoginRegisterDialog();

            BindPublishDialog();

            BindSendMsg();

            BindNewType();

            BindPublishSubmit();
        });
View Code

 

以上是关于邮箱注册登录验证 | 模态对话框 | Django开发的主要内容,如果未能解决你的问题,请参考以下文章

Django用户注册邮箱验证实践

django captcha和邮箱验证机制

Python入门自学进阶-Web框架——16Django登录/注册

Python入门自学进阶-Web框架——16Django登录/注册

Django从零搭建个人博客 | 使用allauth插件管理用户登录与注册

邮箱注册登录 之 激活邮箱 | Django