Django网站用户邮箱验证找回密码

偶尔善良 提交于 2020-01-16 15:41:29

注:Python3,Django1.9

新开一个应用:user_ex,并在settings.py里注册

依赖项:django-simple-captcha (用pip)

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    '......',
    'user_ex',
    'captcha',
]

主路由urls.py包含应用user_ex的路由和依赖项captcha的路由

urls.py

from django.conf.urls import url
from django.conf.urls import include

urlpatterns = [
    url(r'^user_ex/',include('user_ex.urls')),
    url(r'^captcha/',include('captcha.urls')),
]

应用user_ex对应路由配置

user_ex.urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^identify',views.identify,name='identify'),
    url(r'^reset/(.*)/(.*)$',views.reset_password),
    url(r'^modify/',views.modify,name="modify"),
]

后台数据库模型定义

user_ex.models

from django.db import models
from django.utils import timezone


# Create your models here.
class EmailVerifyRecord(models.Model):
    code = models.CharField("动态生成的更改信息的链接后缀",max_length=25)
    email = models.CharField("邮箱",max_length=30)
    valid_time = models.DateTimeField("验证建立时间",null=False,default=timezone.now) #验证码的建立时间,由此确定有效时间段
    class Meta:
        verbose_name_plural = '给邮箱发链接(忘记密码)所用信息'

表单提交对应的格式定义

user_ex.forms

from django import forms
from captcha.fields import CaptchaField

class IdentifyForm(forms.Form):
    email=forms.EmailField(required=True)
    captcha=CaptchaField(error_messages={'invalid':'验证码错误'})

class ResetForm(forms.Form):
    newpwd1=forms.CharField(required=True,min_length=6,error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})
    newpwd2 = forms.CharField(required=True, min_length=6, error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})

配置EMAil_FORM(还需要注册邮箱,登录后开通邮件服务器)

settings.py

#用来发邮件的配置信息(这里用了新浪的)
EMAIL_HOST='smtp.sina.com'
EMAIL_PORT=587
EMAIL_HOST_USER='*******@sina.com' 
EMAIL_HOST_PASSWORD='*********'
EMAIL_USE_TLS=True
EMAIL_FROM='*******@sina.com'  #同上面,你的发送邮件的邮箱

后端响应配套的工具集

user_ex.utils

from random import Random
from .models import EmailVerifyRecord
from django.core.mail import send_mail
from nuckaggle.settings import EMAIL_FROM
 
 
def random_str(randomlength=8): # 获得链接后面的加密参数
    str=''
    chars='AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length=len(chars)-1
    random=Random()
    for i in range(randomlength):
        str+=chars[random.randint(0,length)]
    return str
 
def send_forget_email(email):
    email_record=EmailVerifyRecord()
    code=random_str(16)
    email_record.code=code
    email_record.email=email
    email_record.save()
 
    email_title = '*********网站密码重置链接'
    email_body = '(如非本人操作,请忽略)请点击下面的链接重置你的密码(有效时间:10分钟):http://**主机域名**/user_ex/reset/{0}/{1}'.format(email,code)

    send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
    if send_status:
        return 1
    else:
        return 0

配套的工具模型格式依赖等写好,可以写后端的响应了

user_ex的后端响应

user_ex.views

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import IdentifyForm,ResetForm
from .utils import send_forget_email
from django.contrib.auth.models import User
from .models import EmailVerifyRecord
from django.utils import timezone

# Create your views here.
def home(request):
    return render(request,'account/error.html')

def identify(request):
    context = {}
    identify_from = IdentifyForm()
    if request.method == 'POST':
        form = IdentifyForm(request.POST)
        if form.is_valid():
            email=request.POST.get('email','')
            us = User.objects.filter(email = email)
            if us:
                is_send = send_forget_email(email)
                if is_send:
                    return render(request,'user_ex/send_successful.html')
                else:
                    return render(request,'user_ex/send_fail.html')
            else:
                context["identify_from"] = identify_from
                context["statu"] = 1
                context["error"] = "此邮箱未注册" 
                return render(request,'user_ex/identify.html',context)
        else:
            context["identify_from"] = identify_from
            context["statu"] = 1
            context["error"] = "验证码错误" 
            return render(request,'user_ex/identify.html',context)
    else:
        context["identify_from"] = identify_from
        context["statu"] = 0
        return render(request,'user_ex/identify.html',context)

def reset_password(request,email,active_code): 
    context = {}
    record=EmailVerifyRecord.objects.filter(email = email,code = active_code)
    if record:
        i = record[len(record)-1] #虽然概率极低,有多个的话,取最新的那个(最可能有效)
        create = i.valid_time
        td = timezone.now() - create   #执行这个步骤时now一定比create晚
        if td.seconds//60 > 9  and  td.days < 1:  #链接10分钟内有效
            context['type'] = '链接超时'
            context['message'] = '此链接已经超时失效,请重新获取'
            referer = request.META.get('HTTP_REFERER')
            context["redirect_to"] = referer
            return render(request,'account/error.html',context)
        email=i.email
        user = User.objects.get(email = email)
        return render(request,'user_ex/pass_reset.html',{'email':email,'username':user.username})
    return HttpResponseRedirect('/')

def modify(request):
    reset_form=ResetForm(request.POST)
    if reset_form.is_valid():
        pwd1=request.POST.get('newpwd1','')
        pwd2=request.POST.get('newpwd2','')
        email=request.POST.get('email','')
        if pwd1!=pwd2:
            return render(request,'user_ex/pass_reset.html',{'msg':'密码不一致!','statu':1,'error':"密码不合规,此页面失效,请重新使用邮箱链接"})
        else:
            user=User.objects.get(email=email)
            user.set_password(pwd2)
            user.save()
            return render(request,'user_ex/reset_success.html')
    else:
        email=request.POST.get('email','')
        return render(request,'user_ex/pass_reset.html',{'msg':reset_form.errors,'statu':1,'error':"密码不合规,此页面失效,请重新使用邮箱链接"})

注:if td.seconds//60 > 9 and td.days < 1:

链接10分钟内有效(仔细看下python的timedelta类,只有两个属性seconds和days)

页面模板

acount应用的一个简单的错误页面error.html(写在其他地方也行)

identify.html

{% load static %}
<html>
    <head>
        <title>验证</title>
        <style>
              .cap{
                display: inline-block;
                width: 280px;
                height: 36px;
              }
              .cap img{
                float: right;
              }
        </style>
        <script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
        <script>
            $(function(){
                $('.captcha').css({
                'cursor': 'pointer'
              });
              /*# ajax 刷新*/
                $('.captcha').click(function(){
                  console.log('click');
                  $.getJSON("/captcha/refresh/",function(result){
                    $('.captcha').attr('src', result['image_url']);
                    $('#id_captcha_0').val(result['key'])
                  });
                });
              })
      </script>
    </head>
    <body>
        <h2>邮箱验证修改密码</h2>
        <div class="modal fade" id="register" tabindex="-1" role="dialog">
            <form action="" enctype="multipart/form-data" method="post">  
                  <p><div style="display: inline-block;width:100px;text-align: center"><b >邮箱:</b></div>
                  <div class="cap">{{ identify_from.email }}</div>
                  </p> 

                  <P><div style="display: inline-block;width:100px;text-align: center"><b >验证码:</b></div>
                    <!--验证码start-->
                  <div class="cap">{{ identify_from.captcha }}</div>
                    <!--验证码end-->
                  </P>
                  {% csrf_token %}
                  <div>
                       <label>向您的邮箱发送修改密码的链接</label>
                       <input type="submit" value="发送"> 
                  </div>
            </form>
        </div>
        <script>
                a={{ statu }};
                if (a==1)
                {


                    window.alert('\n\n     {{ error }}\n\n');
                }
        </script>
    </body>
</html>

pass_reset.html

<html>
    <head>
        <title>密码重设</title>
        <style>
            .out{
              width: 500px;
              height: 900px;
              margin: 0 auto;
              margin-top: 100px;
            }
        </style>
    </head>
    <body>
        <h1 style='text-align: center;'>用户&nbsp{{email}}&nbsp的密码重设</h1>
        <h2 style='text-align: center;'>您的用户名为:{{username}}</h2>
        <div class="out">
            <h1>可以重新设置一个好记的新密码啦!</h1>
                <form method="post" action="{% url 'modify' %}">
                  <P><span>新密码:</span><input type="password" name="newpwd1" placeholder="至少6位"></P>
                  <P><span>确认新密码:</span><input type="password" name="newpwd2" placeholder="至少6位"></P>
                  {% csrf_token %}
                  <input type="hidden" name="email" value="{{ email }}">
                  <p><input type="submit" value="确认"></p>
                </form>
            <h1>{{ msg }}</h1>
            <script>
                    a={{ statu }};
                    if (a==1)
                    {


                        window.alert('\n\n     {{ error }}\n\n');
                    }
            </script>
        </div>
    </body>
</html>

reset_success.html

<html>
    <head>
        <title>successful</title>
    </head>
    <body>
        <h1 style='text-align: center;font-size: %222'>successful<br>
        <h2 style='text-align: center'>您的密码已经重置,请牢牢记住新密码</h2>
        </h1>
    </body>
</html>

send_fail.html

<html>
    <head>
        <title>fail</title>
    </head>
    <body>
        <h1 style='text-align: center;font-size: %222'>fail<br>
        <h2 style='text-align: center'>由于网络或其他原因,未成功向邮箱发送密码重置链接,请重试</h2>
        </h1>
    </body>
</html>

send_successful.html

<html>
    <head>
        <title>successful</title>
    </head>
    <body>
        <h1 style='text-align: center;font-size: %222'>successful<br>
        <h2 style='text-align: center'>已经向您的邮箱发送邮件成功,请注意查收(不在收件箱就一定在垃圾箱)</h2>
        </h1>
    </body>
</html>

部分展示

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!