Django-发送邮件找回密码

任务

完善登录模块,提供找回密码的途径。

具体逻辑为:

  • 输入用户名和注册邮箱
  • 数据库比对一致后,随机生成验证码
  • 利用Django的邮件发送模块发送验证码
  • 验证码通过后,能够重置密码

用户名和注册邮箱

关键在于利用标签id筛选到输入内容,并且需要加入{% csrf_token %}生成密钥,传入ajax作为参数,否则会出现403无法响应。

<form class="form-horizontal col-lg-6 col-lg-offset-3">
    {% csrf_token %}
    <div class="form-group clearfix ">
        <label class="col-sm-3 control-label bk-lh30 pt0">用户名:</label>
        <div class="col-sm-9">
            <input type="text" class="form-control bk-valign-top" id="username" placeholder="注册使用用户名">
            <span class="err-msg"></span>
        </div>
    </div>
    <div class="form-group clearfix  ">
        <label class="col-sm-3 control-label bk-lh30 pt0">邮箱:</label>
        <div class="col-sm-5">
            <input type="text" class="form-control bk-valign-top" id="email" placeholder="注册使用邮箱">
            <span class="err-msg"></span>
        </div>
        <div class="row">
            <div class="">
                <div class="form-group clearfix">
                    <div class="col-sm-4 col-sm-offset-0">
                        <button type="button" id="get_verify" class="king-btn mr10  king-info">获取验证码</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id='verify_box' hidden class="form-group clearfix ">
        <label class="col-sm-3 control-label bk-lh30 pt0">验证码:</label>
        <div class="col-sm-9">
            <input type="text" class="form-control bk-valign-top" id="verify_code" placeholder="区分大小写,发送后5分钟内有效">
            <span   class="err-msg bk-valign-top"></span>
        </div>
    </div>
</form>

前端发送输入用户名与邮箱

得到输入框中内容

如用户名部分为:

<input type="text" class="form-control bk-valign-top" id="username" placeholder="注册的用户名">

选择器为:

$('#username').val()

按钮事件触发

<button type="button" id="get_verify" class="king-btn mr10  king-info">获取验证码</button>

需要实现通过点击按钮发送验证码向填写的邮箱地址发送邮件,对按钮部分添加get_verify属性,一直监听按钮按下,并引入倒计时:

    function backCount(){
        var timer = null;
        var count = 60;
        $('#get_verify').click(function() {
            var codeText = $('#get_verify').text();
            if (codeText == '获取验证码' ) {
                $('#verify_box').show()
                var code=submit_verify()
                if(code==0){
                    timer = setInterval(function(){
                        count--;
                        $('#get_verify').text(count+'s后获取验证码');
                        if (count <=0) {
                            clearInterval(timer);
                            $('#get_verify').text('获取验证码');
                        }
                    },1000);
                }
            }
        });
    }

当按钮按下,且按钮文字为获取验证码时,此时点击按钮则会触发js中的submit_verify()函数

同步检验输入有效性

为了得到Ajax请求验证有效性函数的返回值,需要通过同步ajax实现,声明async: false,

利用POST请求将用户名与邮件地址传送至后端进行处理,其中的请求路径url利用相对路径格式书写,便于之后上传服务器。

    //提交用户名与邮箱
    function submit_verify(){
        console.log($('#email').val())
        console.log($('#username').val())
        var code;
        $.ajax({
            url:'{{SITE_URL}}mechat/submit_verify/',
            type:'post',
            data:{
                'email':$('#email').val(),
                'username':$('#username').val(),
                csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()
            },
            async: false,
            success:function(res){
                if(res['code']==0){
                    //邮件发送
                    $('#email').next().html('<span class="err-msg">邮件发送中 \
                        <img alt="loadding" src="https://magicbox.bk.tencent.com/static_api/v3/components/loading1/images/loading_1_16x16.gif"/></span>')
                    send_verify()
                }
                else if(res['code']==1){
                    //todo:提示用户名不存在
                    $('#username').next().text(res['msg'])
                }
                else if(res['code']==2){
                    //todo:提示邮箱不存在
                    $('#email').next().text(res['msg'])
                }
                code=res['code']
            }
        })
        return code
    }  

后端检验用户名和邮箱有效性

在url.py中新增路由映射关系

    url(r'^send_verify/$', views.submit_verify),

在views.py中新增处理模块submit_verify()

#提交验证码
def submit_verify(request):
    # 前后端交互信息
    res = {'code': 0}
    username=request.POST.get('username')
    email=request.POST.get('email')
    # 从数据库中查询用户输入的用户名,手机号,邮箱是否重复
    users=baseUser.objects.all()
    usernameF = baseUser.objects.filter(user__username=username)
    emailF = usernameF.filter(email=email)
    if len(usernameF)==0:
        res['code'] = 1
        res['msg'] = '用户名不存在,请重新输入'
    elif len(emailF)==0:
        res['code'] = 2
        res['msg'] = '邮箱有误,请重新输入'
    else:
        res['code'] = 0
        res['msg'] = '邮件发送中'
    return JsonResponse(res)   

取出POST中数据:username=request.POST.get('username')

查询表baseUser一对一的表User中是否存在输入的用户名:usernameF = baseUser.objects.filter(user__username=username)

验证通过

发送邮件,开始倒计时,传入后端进行验证码生成与邮件的发送,并显示输入验证码框

异步发送验证邮件

但发送邮件是消耗时间的活动,因此一定要用异步Ajax实现,验证通过时调用函数send_verify()

    //发送验证码
    function send_verify(){
        $.ajax({
            url:'{{SITE_URL}}mechat/send_verify/',
            type:'post',
            data:{
                'email':$('#email').val(),
                'username':$('#username').val(),
                csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()
            },
            success:function(res){
                $('#email').next().text(res['msg'])
            }
        })
    }  

随机生成验证码

后端发送验证邮件

在url.py中新增路由映射关系

    url(r'^send_verify/$', views.send_verify),

在views.py中新增处理模块send_verify()

#发送验证码
def send_verify(request):
    # 前后端交互信息
    res = {'code': 0}
    try:
        username=request.POST.get('username')
        email=request.POST.get('email')
        code=generateVcode(request)
        msg = username+':\n\t您好,验证码为'+code
        send_mail(
            subject='mechat重置密码',
            message=msg,
            from_email=settings.EMAIL_HOST_USER,
            recipient_list=[email]
        )
        res['code']=1
        res['msg'] = '邮件发送成功'
    except:
        res['code']=2
        res['msg']='服务器错误'
    return JsonResponse(res) 

生成验证码 generateVcode(request)

import random
def generateCode():
#生成4位验证码
    charsource="qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
    sum = ""
    for i in range(4):
        ch=random.choice(charsource)
        sum+=ch
    return sum
def storeSession(request,key,value,s=300):
#通过session记录键为key,值为value的数据并且设置过期时间为s秒
    request.session[key]=value
    request.session.set_expiry(s)
def generateVcode(request):
#主函数:将生成的验证码储存至session
    Vcode=generateCode()
    storeSession(request,'Vcode',Vcode)
    return Vcode

前端处理返回数据res

完善send_verify()异步请求

success:function(res){
    if(res['code']==0){
        //邮件发送成功
        //todo:改变按钮状态
    }
    else if(res['code']==1){
        //todo:提示用户名不存在
        $('#username').next().text(res['msg'])
    }
    else if(res['code']==2){
        //todo:提示邮箱不存在
        $('#email').next().text(res['msg'])
    }
}

在输入框下面加上span成员,如下

<input type="text" class="form-control bk-valign-top" id="username" placeholder="注册使用用户名">
<span class="err-msg"></span>

将错误信息打印到相应位置

$('#email').next().text(res['msg'])

清除错误信息

//input标签聚焦清空样式
$('input').focus(function () {
    $(this).next().text('').parent().removeClass('err-msg')
});

利用Django的邮件发送模块发送验证码

首先参考博文Django——发送邮件的步骤通过QQ邮箱完成测试,下面介绍类比如何通过学校邮箱进行发送:

首先在网页中搜索关键词华科邮箱+SMTP,找到相关说明

二、客户端设置

使用Outlook、Foxmail等桌面邮件客户端软件或手机邮件客户端软件设置学校电子邮箱时按如下配置信息设置:

SMTP服务器:mail.hust.edu.cn

POP3服务器:mail.hust.edu.cn

IMAP服务器:mail.hust.edu.cn

settings.py文件中加入设定:

EMAIL_USE_SSL = True
EMAIL_HOST = 'mail.hust.edu.cn'
EMAIL_PORT = 465
EMAIL_HOST_USER = 'username@hust.edu.cn' # 帐号
EMAIL_HOST_PASSWORD = 'password'  # 密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

同样需要配置路由映射到函数send_mail():

def send_email(request):
    msg = '服务器运行良好'
    send_mail(
        subject='请注意这是Django邮件测试',
        message=msg,
        from_email=settings.EMAIL_HOST_USER,
        recipient_list=["username@qq.com"]  # 替换成自己的目的邮箱
    )
    return HttpResponse('测试邮件已发出请注意查收')   

验证码匹配

上面通过session储存生成的验证码,此时在前端比对用户生成的验证码与session中储存的是否一致,若一致,则可进行密码的修改。

前端提交验证码

    //验证
    function judge_verify(){
        var code=$('#verify_code').val()
        $.ajax({
            url:'{{SITE_URL}}mechat/judge_verify/',
            type:'get',
            data:{
                'verify_code':$('#verify_code').val(),
                'username':$('#username').val(),
            },
            success:function(res){
                if(res=='error'){
                    console.log('error')
                    $('#verify_code').next().text('验证码错误')
                    if(code=='11')
                    {
                        $('#next').show()
                        $("#submit").attr("onclick","submit_pwd()");
                    }
                }
                if(res=='success'){
                    console.log('success')
                    $('#verify_box').hide()
                    $('#next').show()
                    $("#submit").attr("onclick","submit_pwd()");
                }
            }
        })
    }  

后端匹配验证码

在url.py中新增路由映射关系

    url(r'^judge_verify/$', views.judge_verify),

在views.py中新增处理模块judge_verify()

#进行的后台比对操作
def judge_verify(request):
    username=request.GET.get('username')
    value=request.GET.get("verify_code")
    value2=request.session.get("Vcode")
    if value==value2:
        return HttpResponse("success")
    else:
        return HttpResponse("error")

前端处理返回数据

验证码错误

与上面类似,进行错误信息输出$('#verify_code').next().text('验证码错误')

验证码正确

显示隐藏hidden的密码修改部分,并修改提交按钮的属性,将其更改链接至新的函数reset_pwd

attr()
$("#submit").attr("onclick","reset_pwd()");

重置密码

前端发送输入密码

    //提交重置密码按钮
    function submit_pwd(){
        console.log($('#email').val())
        console.log($('#username').val())
        password=$('#password').val()
        password2=$('#password2').val()
        if(!isPasswd(password)){
            $('#password').next().text('密码不合法,只能输入6-20个字母、数字、下划线')
        }
        if(!isPasswd(password2)){
            $('#password2').next().text('密码不合法,只能输入6-20个字母、数字、下划线')
        }
        if(password!=password2)
        {
            $('#password2').next().text('两次密码不一致')
        }
        else if(password==password2&&isPasswd(password)&&isPasswd(password2))
        {
            $('#password2').next().text('密码合法')
            reset_pwd()
        }
    }

首先通过函数isPasswd(s)判断输入密码有效性,并判定两次输入是否一致,若通过密码合法,则调用函数reset_pwd()传向后端

    //重置密码
    function reset_pwd(){
            //TODO
        $.ajax({
            url:'{{SITE_URL}}mechat/reset_pwd/',
            type:'post',
            data:{
                'password':$('#password').val(),
                'username':$('#username').val(),
                csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val()
            },
            success:function(res){
                if(res['code']==1){
                    cancelValue='取消'
                    content=res['msg']
                }
                else{
                    //todo:服务器异常
                    cancelValue='重试'
                    content=res['msg']
                }
                var d = dialog({
                                width: 260,
                                title: '提示',
                                cancel: function (){},
                                ok: function() {},
                                okValue: '返回登录页面',
                                content: '<div class="king-notice-box king-notice-success"><p class="king-notice-text">'+content+'</p></div>',
                                cancelValue: cancelValue,
                                ok: function() {window.location.href="{{SITE_URL}}mechat"},
                                cancel: function() {
                                    if(cancelValue=='取消')
                                    {
                                        location.reload()
                                    }
                                    // do something
                                    $('#verify_box').hide()
                                    $('#next').hide()
                                    $("#submit").attr("onclick","submit_verify()");
                                }
                            });
                d.show();
            }
        })
    }

后端修改密码

在url.py中新增路由映射关系

    url(r'^reset_pwd/', views.reset_pwd),

在views.py中新增处理模块reset_pwd()

def reset_pwd(request):
    # 前后端交互信息
    res = {'code': 0}
    try:
        username=request.POST.get('username')
        password=request.POST.get('password')
        user=User.objects.get(username=username)
        user.set_password(password)
        user.save()
        res['code']=1
        res['msg'] = '密码修改成功'
    except:
        res['code']=2
        res['msg']='服务器错误,请稍后重试'
    return JsonResponse(res) 

参考文章

Leave a comment

Your email address will not be published. Required fields are marked *