1. 程式人生 > >Django第5章: auth補充之用戶註冊,密碼找回

Django第5章: auth補充之用戶註冊,密碼找回

war def title 必須 elb errors res random 綁定

自定義以郵箱和密碼登錄用戶

1. 重載authenticate()

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

from userinfo.models import UserProfile

class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        # 以username或者email字段匹配用戶輸入的usernma內容
        user = UserProfile.objects.filter(Q(username=username)|Q(email=username)).first()

        # 匹配出用戶記錄後再進一步匹配密碼
        if user.check_password(password):
            # 返回用戶
            return user
        else:
            return None

2. 修改配置文件

AUTHENTICATION_BACKENDS = (
    'userinfo.views.CustomBackend',
)

外部驗證碼模塊應用

django-simple-captcha模塊

  1. 安裝模塊: pip install django-simple-captcha;
  2. 增加captchaINSTALLED_APPS中;
  3. 生成captcha表: manage.py migrate;
  4. 配置urls.py:

    urlpatterns += [
    url(r'^captcha/', include('captcha.urls')),
    ]
  5. 設置cpatchamodel

    from captcha.fields import CaptchaField
    from django import forms
    class RegisterForm(forms.Form):
        username = forms.CharField(max_length=12, min_length=2, required=True)
        password = forms.CharField(min_length=4, required=True)
        captcha = CaptchaField()
  6. 在模板直接導入 {{RegisterForm.captcha}}

    即可顯示對應的圖片和輸入框還有隱藏的框

  7. 驗證碼的錯誤這個模塊會自行進行校驗,無須自己手動寫邏輯;

註冊邏輯
// views.py
class RegisterView(View):
    def get(self, request):
        register_form = RegisterForm()
        return render(request, 'register.html', {
            'register_form': register_form,
        })

    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            email = register_form.cleaned_data.get('email', '')
            // 過濾重復註冊
            if UserProfile.objects.filter(email=email):
                return render(request, 'register.html', {'msg':'用戶已註冊', 'register_form':register_form})
            else:
                password = register_form.cleaned_data.get('password', '')
                
                # 新建用戶
                new_user = UserProfile()
                new_user.username = email
                new_user.email = email
                
                # 非常重要, 用戶沒有激活鏈接前必須為False
                new_user.is_active = False
                
                # 對密碼進行加密處理,利用django自帶的加密方法
                new_user.password = make_password(password)
                new_user.save()

                # 發送郵件進行激活驗證
                send_email(email, 'register')
                
                return render(request, 'send_email_success.html')

        else:
            # form自行檢測字段不合格
            return render(request, 'register.html', {'register_form': register_form})
// register.html

<form  method="post" action="{% url 'register' %}" autocomplete="off">
    <!--若用戶名form表單出現錯誤信息,則加上errorput屬性-->
    <div class="form-group marb20 {% if register_form.errors.email %}errorput{% endif %}">
        <label>郵&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;箱</label>
        <input  type="text" id="id_email" name="email" value="" placeholder="請輸入您的郵箱地址" autocomplete="On" />
    </div>
    
    <!--同上-->
    <div class="form-group marb8 {% if register_form.errors.password %}errorput{% endif %}">
        <label>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;碼</label>
        <input type="password" id="id_password" name="password"  value="" placeholder="請輸入6-20位非中文字符密碼" />
    </div>
    
    <div class="form-group marb8 captcha1 {% if register_form.errors.captcha %}errorput{% endif %}">
        <label>驗&nbsp;證&nbsp;碼</label>
        {{ register_form.captcha }}
    </div>
    
    <!-- 循環列出表單的報錯信息 -->
    <div class="error btns" id="jsEmailTips"> {{ msg }} {% for key, value in register_form.errors.items %} {{ key }}{{ value }} {% endfor %}</div>
    
    <div class="auto-box marb8">
    </div>
    <input class="btn btn-green" id="jsEmailRegBtn" type="submit" value="註冊並登錄" />
    {% csrf_token %}
</form>

發送郵件

setting文件配置
EMAIL_HOST = "smtp.qq.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = '[email protected]'
EMAIL_HOST_PASSWORD = 'qgmlmnhulqupbdij'
EMAIL_USE_TLS = True
EMAIL_FROM = EMAIL_HOST_USER
發送郵件邏輯
# send_email.py
// 由激活視圖函數通過驗證後才調用此函數

from datetime import datetime
import random, string
from django.core.mail import send_mail  # django自帶的郵件發送模塊
from onlineLearningSys.settings import EMAIL_FROM
from userinfo.models import EmailVerifyRecord


def generate_random_str(num):
    # 生成特定位數的字符串
    return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(num))


def send_email(email, send_type = ''):
    if type == "change_email":
        code = generate_random_str(4)
    else:
        code = generate_random_str(16)
    
    # 實例化郵件激活碼對象,並在數據庫生成記錄
    email_verify_record = EmailVerifyRecord()
    email_verify_record.email = email
    email_verify_record.code = code
    email_verify_record.send_type = send_type
    email_verify_record.send_time = datetime.now()
    email_verify_record.save()

    email_title = ""
    email_body = ""
    if send_type == 'register':
        email_title = 'MXonline在線網註冊激活連接'
        email_body = '請點擊下面的鏈接重置密碼: http://127.0.0.1:8000/user/activate/{0}'.format(code)
        # 固定格式書寫
        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        if send_status:
            print('發送成功')

    elif send_type == 'forget':
        email_title = 'MXonline在線網密碼重置連接'
        // 當用戶點擊此網址時候,會觸發對應的視圖函數,並且接收到對應的值;
        email_body = '請點擊下面的鏈接激活的你的賬號: http://127.0.0.1:8000/user/reset/{0}'.format(code)
        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        if send_status:
            print('發送成功')

    elif send_type == 'change_email':
        email_title = 'MXonline在線網綁定郵箱更改鏈接'
        email_body = '請復制您的驗證碼 : {0}'.format(code)
        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        if send_status:
            print('發送成功')

激活用戶

  1. 每發送一次郵件, 都會生成email_verify_record對象,包括email,codesend_type三個字段,保存在數據庫;

  2. 配置url(r‘^activate/(\S{16})/‘, ActivateView.as_view(), name=‘activate‘),, 由對應的視圖函數接收16位驗證碼;

  3. 激活邏輯

    class ActivateView(View):
        def get(self, request, code):
            email_record = EmailVerifyRecord.objects.filter(code=code)
    
            # 根據傳過來的code在數據庫中取出相應的郵件激活記錄
            if email_record:
                record = email_record.first()
                email = record.email
                user = UserProfile.objects.get(email=email)
                user.is_active = True
                print('激活成功')
                user.save()
                return redirect('/user/login/')
    
            else:
                return render(request, 'register.html')

密碼找回

  1. 若忘記密碼.需要提交郵箱和驗證碼,進行發送郵件

    class ForgetPwd(View):
        # 忘記密碼
        def get(self, request):
            forget_p_form = ForgetPwdForm()
            return render(request, 'forgetpwd.html', {'forget_p_form': forget_p_form})
    
    
        def post(self, request):
            forget_p_form = ForgetPwdForm(request.POST)
            if forget_p_form.is_valid():
                email = forget_p_form.cleaned_data.get("email", '')
    
                # 根據郵箱信息進行用戶過濾
                user = UserProfile.objects.filter(Q(username=email)|Q(email=email))
                if user:
                    send_email(email, 'forget')
                    return render(request, 'send_email_success.html')
    
                else:
                    # 用戶不存在
                    return render(request, 'forgetpwd.html', {'forget_p_form': forget_p_form, 'msg': '對不起,該郵箱未被註冊'})
    
            else:
                return render(request, 'forgetpwd.html', {'forget_p_form': forget_p_form})
  2. 用戶收到激活地址後,進入重置密碼頁面,此時將email渲染如模板中

    # 第一次訪問時候先將用戶的email的值渲染進模板裏保存起來
    urlpatterns = [url(r'^reset/(\S{16})/', ResetPwd.as_view()),] 
    
    # 此處必須單獨寫一個get函數,僅限於用戶第一次點擊激活地址進入
    class ResetPwd(View):
        def get(self, request, code):
            # 進入密碼重置界面
            # 獲取郵箱驗證記錄
            email_record = EmailVerifyRecord.objects.filter(code=code)
            if email_record:
                record = email_record.first()
                email = record.email
                # 通過註冊郵箱找到用戶
                user = UserProfile.objects.filter(email=email)
                if user:
                    # 存在相關用戶,跳轉至登錄頁面
                    return render(request, 'password_reset.html', {"email":email})
    
    
    // html
    <form id="reset_password_form" action="{% url 'reset-pwd' %}" method="post">
        <ul>
            <li>
                <span class="">新 密 碼 :</span>
                <input type="password" name="password_1" id="pwd" placeholder="6-20位非中文字符">
                <i>{{ pwd_reset_form.errors.password_1 }}</i>
            </li>
    
            <input name="email" type="hidden" value="{{ email }}">
    
            <li>
                <span class="">確定密碼:</span>
                <input type="password" name="password_2" id="repwd" placeholder="6-20位非中文字符">
                <i>{{ pwd_reset_form.errors.password_1 }}</i>
                <i>{{ msg }}</i>
            </li>
    
            <li class="button">
                <input type="submit" value="提交" onclick="reset_password_form_submit()">
            </li>
        </ul>
        {% csrf_token %}
    </form>
  3. 提交重置表單

    // urls.py
    urlpatterns = [url(r'^reset/', ResetPwdView.as_view(), name='reset-pwd'), ]
    
    // views.py
    class ResetPwdView(View):
        def post(self, request):
            pwd_reset_form = PwdResetForm(request.POST)
            email = request.POST.get('email', '')
            if pwd_reset_form.is_valid():
                password_1 = pwd_reset_form.cleaned_data.get('password_1')
                password_2 = pwd_reset_form.cleaned_data.get('password_2')
                if password_1 == password_2:
                    user = UserProfile.objects.filter(Q(email=email)|Q(username=email))
                    if user:
                        user = user.first()
                        user.password = make_password(password_1)
                        user.save()
                        print('密碼修改成功')
                        return render(request, 'login.html')
                    else:
                        # 用戶不存在
                        return render(request, 'password_reset.html', {'msg':'用戶不存在'})
                else:
                    return render(request, 'password_reset.html', {'msg': '密碼輸入不一致', 'pwd_reset_form':pwd_reset_form, 'email':email})
    
            else:
                return render(request, 'password_reset.html', {'pwd_reset_form':pwd_reset_form, 'email':email})

Django第5章: auth補充之用戶註冊,密碼找回