天天看點

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

Django 項目實踐 —— 可重用注冊登入系統 (2)

目錄

Django 項目實踐 —— 可重用注冊登入系統 (2)

圖檔驗證

Django表單

郵箱注冊

發送郵件功能測試

基本的注冊功能實作

系統資料庫單

實作注冊視圖

注冊添加密碼加密功能

郵件注冊确認

建立模型

處理郵件确認請求

修改登入規則

圖檔驗證

為了防止機器人頻繁登入網站或者破壞分子惡意登入,很多使用者登入和注冊系統都提供了圖形驗證碼功 能。 在Django中實作圖檔驗證碼功能非常簡單,有現成的第三方庫可以使用,我們不必自己開發(不必重複 造輪子)。這個庫叫做django-simple-captcha。 具體安裝教程: 戳我

Django表單

我們前面都是手工在HTML檔案中編寫表單form元素,然後在views.py的視圖函數中接收表單中的使用者 資料,再編寫驗證代碼進行驗證,最後使用ORM進行資料庫的增删改查。這樣費時費力,整個過程比較 複雜,而且有可能寫得不太恰當,資料驗證也比較麻煩。設想一下,如果我們的表單擁有幾十上百個數 據字段,有不同的資料特點,如果也使用手工的方式,其效率和正确性都将無法得到保障。有鑒于此, Django在内部內建了一個表單功能,以面向對象的方式,直接使用Python代碼生成HTML表單代碼,專 門幫助我們快速處理表單相關的内容。 Django的表單給我們提供了下面三個主要功能:

  • 準備和重構資料用于頁面渲染;
  • 為資料建立HTML表單元素;
  • 接收和處理使用者從表單發送過來的資料。

編寫Django的form表單,非常類似我們在模型系統裡編寫一個模型。在模型中,一個字段代表資料表 的一列,而form表單中的一個字段代表<form>中的一個 <input> 元素

戳我了解更多Django表單操作

建立表單模型

# /login/forms.py(建立的檔案)


from captcha.fields import CaptchaField
from django import forms
class LoginForm(forms.Form):
    username = forms.CharField(label='使用者名', required=True,min_length=4,max_length=128)
    password = forms.CharField(label="密碼", required=True,min_length=4, max_length=10)
    captcha = CaptchaField(label="驗證碼")
           

視圖邏輯優化

# login/views.py


def login(request):
    # 請求方法為POST送出
    if request.method == 'POST':
        # 修改1: 執行個體化表單對象
        login_form = LoginForm(request.POST)
        # 修改2: 驗證表單資料的合法性
        if login_form.is_valid():
            # 修改3:擷取表單填寫的資料,資料清洗
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            user = SiteUser.objects.filter(name=username,password=password).first()
            if user:
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return redirect('/index/')
            else:
                message = "使用者名或者密碼錯誤"
                # 修改4: locals()以字典方式傳回目前所有的變量
                # eg:{'message':'xxxx', 'login_form':'xxx'}
                return render(request, 'login/login.html', locals())
        else:
            message = "填寫的登入資訊不合法"
            return render(request, 'login/login.html', locals())
# 請求方法是GET請求
login_form = LoginForm()
return render(request, 'login/login.html', locals())
           

Template頁面優化

# templates/login/login.html(部分修改)


<h3 style="text-align: center">使用者登入</h3>

# 修改1: 不同的報錯,提示不同的資訊
{% if login_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
    <strong>登入失敗!</strong> 驗證碼不正确
</div>

{% elif message %}
<div class="alert alert-warning" role="alert">
    <strong>登入失敗!</strong> {{ message }}
</div>

{% endif %}
<div class="form-group">
    # 修改2:
    <label>{{ login_form.username.label }}</label>
    <input type="text" class="form-control" name="username">
</div>

<div class="form-group">
    # 修改3:
    <label>{{ login_form.password.label }}</label>
    <input type="password" class="form-control" name="password">
    <small class="form-text text-muted">密碼必須是字母、數字或者特殊符号組成.</small>
</div>

# 修改4: 最重要的,添加驗證碼表單
<div class="form-group">
    <label>{{ login_form.captcha.label }}</label>
    {{ login_form.captcha }}
</div>
           

 儲存到本地git 庫

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

驗證是否正确

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

資訊錯誤時(提示相應的提示)

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

郵箱注冊

發送郵件功能測試

配置郵件資訊

#  LoginRegister/settings.py
# 添加部分如下


# mail configure
EMAIL_HOST = 'smtp.163.com'  # 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '自己的郵箱位址'
EMAIL_HOST_PASSWORD = '開啟服務後,提示的授權碼'
EMAIL_USE_SSL = False

# 确認郵件失效的時間
CONFIRM_DAYS = 3
           

如何擷取授權碼

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

互動式環境中測試發送郵件是否成功?(安裝一個ipython,友善互動式的使用,可以補齊。pip install ipython )

Terminal輸入指令>  python manage.py shell

In [1]: from django.core.mail import send_mail
In [2]: from loginRegister.settings import EMAIL_HOST_USER
In [3]: send_mail("測試郵件", "content", EMAIL_HOST_USER,['目的郵箱位址',])
           
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

驗證是否成功? 檢視是否受到郵件?

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

基本的注冊功能實作

系統資料庫單

實作注冊視圖

  • 如果使用者已經登入,則不能注冊跳轉到首頁。
  • 如果是GET請求,傳回使用者注冊的html頁面。
  • 如果是POST請求, 先驗證送出的資料是否通過,清洗資料。 接下來判斷使用者名和郵箱是否已經被注冊, 将注冊的資訊存儲到資料庫,跳轉到登入界面。
  • 額外功能: 為了資料的安全性注冊時,密碼存儲到資料庫不是明文存儲,而是先加密再存儲。
# login/views.py


def register(request):
    # 如果使用者已經登入,則不能注冊跳轉到首頁。
    if request.session.get('is_login', None):
        return redirect('/index/')
# 如果是POST請求
if request.method == 'POST':
    print(request.POST)
    register_form = RegisterForm(request.POST)
    message = "請檢查填寫的内容!"
    # 先驗證送出的資料是否通過
    if register_form.is_valid():
        # 清洗資料
        username = register_form.cleaned_data.get('username')
        password1 = register_form.cleaned_data.get('password1')
        password2 = register_form.cleaned_data.get('password2')
        email = register_form.cleaned_data.get('email')
        print(locals())
        # 接下來判斷使用者名和郵箱是否已經被注冊
        same_name_user = SiteUser.objects.filter(name=username)
        print(same_name_user)
        if same_name_user:
            message = '使用者名已經存在'
            return render(request, 'login/register.html', locals())
        same_email_user = SiteUser.objects.filter(email=email)
    if same_email_user:
        message = '該郵箱已經被注冊了!'
        return render(request, 'login/register.html', locals())
    # 将注冊的資訊存儲到資料庫,跳轉到登入界面
    new_user = SiteUser(name=username, password=password1, email=email)
    new_user.save()
    return redirect('/login/')
# 如果是GET請求,傳回使用者注冊的html頁面。
register_form = RegisterForm()
return render(request, 'login/register.html', locals())
           

Template模闆的更改

# templates/login/register.html



<h3 style="text-align: center">使用者注冊</h3>
{% if register_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
    <strong>注冊失敗!</strong> 驗證碼不正确
</div>
{% elif message %}
<div class="alert alert-warning" role="alert">
    <strong>注冊失敗!</strong> {{ message }}
</div>


{% endif %}
<form action="/register/" method="post">
    {% csrf_token %}
    <div class="form-group">
        <label>{{ register_form.username.label }}</label>
        <input type="text" class="form-control" name="username">
    </div>
    <div class="form-group">
        <label>{{ register_form.email.label }}</label>
        <input type="email" class="form-control" name="email">
    </div>
    <div class="form-group">
        <label>{{ register_form.password1.label }}</label>
        <input type="password" class="form-control" name="password1">
        <small class="form-text text-muted">密碼必須是字母、數字或者特殊符号組成。</small>
</div>
<div class="form-group">
    <label>{{ register_form.password2.label }}</label>
    <input type="password" class="form-control" name="password2">
    <small class="form-text text-muted">密碼必須是字母、數字或者特殊符号組成.</small>
</div>
<div class="form-group">
    <label>{{ register_form.captcha.label }}</label>
    {{ register_form.captcha }}
</div>
<a href="/login/" target="_blank" rel="external nofollow"  class="text-success">
    <ins>使用者登入</ins>
</a>
<button type="submit" class="btn btn-primary float-right">注冊</button>
</form>
           

測試是否成功

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

 資料庫是否寫入

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

 儲存到本地git庫

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

注冊添加密碼加密功能

對于如何加密密碼,有很多不同的途徑,其安全程度也高低不等。這裡我們使用Python内置的hashlib 庫,使用哈希值的方式加密密碼,可能安全等級不夠高,但足夠簡單,友善使用。

在 login/utils.py(建立) 中編寫一個hash函數:

def hash_code(s, salt='mysite'):# 加點鹽
    h = hashlib.sha256()
    s += salt
    h.update(s.encode()) # update方法隻接收bytes類型
    return h.hexdigest()
           

在 login/views.py 中修改login和register視圖

def register(request):
    # .....省略部分代碼
    new_user = SiteUser(name=username, password=hash_code(password1),email=email)
    # .....省略部分代碼

def login(request):
    # .....省略部分代碼
    user = SiteUser.objects.filter(name=username,password=hash_code(password)).first()
    # .....省略部分代碼
           

測試,新注冊使用者,資料庫的密碼是否加密

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

儲存到本地 git庫

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

郵件注冊确認

很自然地,我們會想到如果能用郵件确認的方式對新注冊使用者進行審查,既安全又正式,也是目前很多 站點的做法。

建立模型

既然要區分通過和未通過郵件确認的使用者,那麼必須給使用者添加一個是否進行過郵件确認的屬性。 另外,我們要建立一張新表,用于儲存使用者的确認碼以及注冊送出的時間。

# /login/models.py


class SiteUser(models.Model):
    # .......
    has_confirmed = models.BooleanField(default=False, verbose_name="是否郵箱驗證")


class ConfirmString(models.Model):
    code = models.CharField(max_length=256, verbose_name="确認碼")
    user = models.OneToOneField('SiteUser', on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="建立時間")
    def __str__(self):
        return self.user.name + ":" + self.code

    class Meta:
        ordering = ["-create_time"]
        verbose_name = "确認碼"
        verbose_name_plural = "确認碼"
           

資料庫模型更改,一定要生成遷移腳本和寫入資料庫。

python manage.py makemigrations
python manage.py migrate
           

順便修改一下admin.py檔案,友善我們在背景修改和觀察資料

# login/admin.py


admin.site.register(ConfirmString)
           

修改視圖

# login/views.py


def register(request):
    # ................
    code = make_confirm_string(new_user)
    send_email(email, code)
    message = '請前往郵箱進行确認!'
    # ..................
           

make_confirm_string() 是建立确認碼對象的方法,代碼如下

# login/utils.py


import datetime
def make_confirm_string(user):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    models.ConfirmString.objects.create(code=code, user=user,)
    return code
           

注: 這裡确認碼是日期時間,需要将 settings.py 中  USE_TZ = True 改為 USE_TZ = False,否則可能會出錯

send_email(email, code) 方法接收兩個參數,分别是注冊的郵箱和前面生成的哈希值,代碼如下:

def send_email(email, code):
    print('send mail.........')
    subject = '注冊确認郵件'
    text_content = '''感謝注冊,這裡是登入注冊系統網站!\
            如果你看到這條消息,說明你的郵箱伺服器不提供HTML連結功能,請聯系管理者!'''
    html_content = '''
    <p>感謝注冊<a href="http://{}/confirm/?code={}" target="_blank" rel="external nofollow"  target=blank>點選驗證</a>,\
    這裡是登入注冊系統網站!</p>
    <p>請點選站點連結完成注冊确認!</p>
    <p>此連結有效期為{}天!</p>
    '''.format('127.0.0.1:9999', code, settings.CONFIRM_DAYS)

    send_mail(subject, text_content,settings.EMAIL_HOST_USER, [email, ], html_message=html_content)
           

最後的有效期天數為設定在settings中的 CONFIRM_DAYS 。下面是郵件相關的settings配置:

LoginRegister/settings.py


# 注冊有效期天數
CONFIRM_DAYS = 3
           

測試:注冊一個使用者,判斷是否能收到确認郵件。

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

儲存到本地 git 庫

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

處理郵件确認請求

在login子應用的 urls.py 中添加一條url:

path('confirm/', views.user_confirm,name='confirm'),
           

其次,在 login/views.py 中添加一個 user_confirm 視圖。

  • 擷取确認碼資訊
  • 資料庫中是否有該确認碼,如果沒有, 傳回說是無效的請求
  • 資料庫中是否有該确認碼,如果有, 判斷是否過期? 如果過期,删除使用者資訊,否則更新使用者資訊。
# login/views.py


# 郵件驗證
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = ConfirmString.objects.get(code=code)
    except:
        message = '無效的确認請求!'
        return render(request, 'login/confirm.html', locals())
    
    create_time = confirm.create_time
    now = datetime.now()
    print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS))
    if now > create_time + timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的郵件已經過期!請重新注冊!'
    else:
        confirm.user.has_confirmed = True
        confirm.user.save()
        confirm.delete()
        message = '感謝确認,請使用賬戶登入!'
        return render(request, 'login/confirm.html', locals())
           

需要一個 confirm.html 頁面,我們将它建立在 /login/templates/login/ 下面: 頁面中通過JS代碼,設定2秒後自動跳轉到登入頁面,可根據自己的需要去除或者美化。

# login/templates/login/confirm.html



<h1 style="margin-left: 100px;">{{ message }}</h1>
<script>
window.setTimeout("window.location='/login/'",2000);
</script>
           

修改登入規則

既然未進行郵件确認的使用者不能登入,那麼我們就必須修改登入規則,如下所示:

def login(request):
    ......
            if user:
                # 修改的部分
                # 使用者的code (确認碼)是否存在,has_confirmed 預設 False 
                if not user.has_confirmed:
                    message = '該使用者還未經過郵件确認!'
                    return render(request, 'login/login.html', locals())
......
           

測試,通過郵箱驗證注冊一個新使用者

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

未郵箱驗證的賬戶登入,會提示,使用者為經過郵箱驗證

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

在郵箱裡進行驗證

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

驗證成功後再次登入

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認
Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

添加到本地 git 庫

Django 項目實踐 —— 可重用注冊登入系統 (2)Django 項目實踐 —— 可重用注冊登入系統 (2)圖檔驗證 Django表單 郵箱注冊 基本的注冊功能實作郵件注冊确認

至此一個可重用注冊登入系統完成......