[django項目] 用戶登錄登出功能

用戶登錄登出功能

I. 功能需求分析

1>功能分析

1.1>流程圖

[外鏈圖片轉存失敗(img-Fvz7ob4M-1565741008283)(用戶登錄登出功能.assets/登錄註冊流程圖 (2)]-1562769060760.png)

1.2>功能接口

  1. 登錄頁面
  2. 登錄功能
  3. 退出功能

II. 登陸頁面

1>接口設計

1.1>接口說明

類目 說明
請求方法 GET
url定義 /users/login/
參數格式 無參數

1.2>返回結果

登陸頁面

2.後端代碼

  1. user/views.py代碼:

    from django.shortcuts import render
    from django.views import View
    # ....
    
    class LoginView(View):
        """
        登錄視圖
        """
        def get(self, request):
            return render(request, 'users/login.html')
    
  2. user/urls.py代碼:

    from django.urls import path, include
    from . import views
    app_name = 'users'
    
    urlpatterns = [
        path('register/', views.RegisterView.as_view(), name='register'),
        path('login/', views.LoginView.as_view(), name='login'),
    ]
    

3.前端頁面代碼

1.users/login.html

因爲表單需要post請求,所以記得在頁面使用{% csrf_token %}標籤。

註冊功能已經開發好了,所以頁面的上的立即註冊處的a標籤href可以寫好

{% extends 'base/base.html' %}
{% load static %}

{% block title %}登錄{% endblock title %}
{% block link %}
      <link rel="stylesheet" href="{% static 'css/users/auth.css' %}">
{% endblock link %}

{% block main_start %}
    <!-- container start -->
<main id="container">
  <div class="login-contain">
    <div class="top-contain">
      <h4 class="please-login">請登錄</h4>
      <a href="{% url 'users:register' %}" class="register">立即註冊 &gt;</a>
    </div>
    <form action="" method="post" class="form-contain">
      <div class="form-item">
        <input type="tel" placeholder="請輸入用戶名或手機號" name="account" class="form-control" autocomplete="off">
      </div>
      <div class="form-item">
        <input type="password" placeholder="請輸入密碼" name="password" class="form-control">
      </div>
      <div class="form-item clearfix">
        <label>
          <input type="checkbox" name="remember">
          <span>記住我</span>
        </label>
        <a href="javascript:void(0);" class="forget-password">忘記密碼?</a>
      </div>
      <div class="form-login">
        <input type="submit" value="登錄" class="login-btn">
      </div>
        {% csrf_token %}
    </form>
  </div>
</main>
<!-- container end -->
{% endblock main_start %}

III. 登錄功能

1>業務流程分析

  1. 參數校驗
    1. 賬戶校驗(用戶名/手機號)
    2. 密碼校驗
    3. 賬戶密碼聯合校驗
  2. 登陸邏輯
    1. 登陸就是在session中保存狀態
    2. 根據記住我選項, 設置sessionID有效期

2>接口設計

2.1>接口說明:

類目 說明
請求方法 POST
url定義 /users/login/
參數格式 表單參數

2.2>參數說明:

參數名 類型 是否必須 描述
account 字符串 用戶輸入的手機號或用戶名
password 字符串 用戶輸入的密碼
remember 字符串 用戶是否選擇免登錄

2.3>返回結果:

{
    "errno": "0", 
 	"errmsg": "OK", 
}

雖然登陸功能簡單, 但安全需求極高, 需要考慮到的底層的東西很多, 所有這裏我們可以使用django內置的auth模塊來實現, 目的追求的是快速開發, 不深入底層

3>後端代碼

3.1>users/views.py視圖

在users目錄下的views.py文件中定義如下視圖:

class LoginView(View):
    """
    登錄視圖
    """
    def get(self, request):
        return render(request, 'user/login.html')

    def post(self, request):
        # 1.校驗參數
        form = LoginForm(request.POST, request=request)
        if form.is_valid():
            # 2.登錄功能
            return json_response(errmsg='恭喜登錄成功!')
        else:
            # 引發的錯誤可能會有多條錯誤信息,如果出錯了,就將表單的報錯信息進行拼接,
            err_msg_list = []
            for item in form.errors.values():
                # 遍歷的item也是一個列表, 其中的第一個元素是報錯信息
                err_msg_list.append(item[0])
            err_msg_str = '/'.join(err_msg_list)
            # 由於是參數錯誤, 所以使用Code.PARAMERR
            return json_response(errno=Code.PARAMERR, errmsg=err_msg_str)

3.2>users/forms.py表單

在user目下的forms.py文件中定義如下表單:

import re

from django import forms
from django.db.models import Q
from django.contrib.auth import login

from users.models import User
from .constants import USER_SESSION_EXPIRY

class LoginForm(forms.Form):
    # 由於不確定是手機號碼還是用戶名,而這兩者的格式是不一樣的,所以要單獨校驗
    account = forms.CharField(error_messages={'required': '賬戶不能爲空'})
    password = forms.CharField(max_length=20, min_length=6,
                               error_messages={
                                   'max_length': '密碼長度要小於20',
                                   'min_length': '密碼長度要大於6',
                                   'require': '密碼不能爲空'
                               })
    remember = forms.BooleanField(required=False)

    def __init__(self, *args, **kwargs):    # 在不清楚有什麼具體參數的情況下,可使用這種方式代替
        self.request = kwargs.pop('request', None)    # 將接收到的request從參數中剔除出來,然後賦值
        # 執行完成上面的操作後再去執行父類的init,從而不會破環父類的初始化
        super().__init__(*args, **kwargs)


    def clean_account(self):
        """
        校驗用戶賬戶
        :return:
        """
        account = self.cleaned_data.get('account')
        if not re.match(r'^1[3-9]\d{9}$', account) and (len(account)<5 or len(account)>20):
            raise forms.ValidationError('用戶賬戶格式不正確,請重新輸入')
        # 一定要return
        return account

    def clean(self):
        """
        校驗用戶名密碼,並實現登錄邏輯
        :return:
        """
        clean_data = super().clean()

        # 拿到參數
        account = clean_data.get('account')
        password = clean_data.get('password')
        remember = clean_data.get('remember')

        # 實現登陸邏輯
        # 判斷用戶名與密碼是否匹配
        # 1. 先找到這個用戶, 使用以下代碼來試下這一句sql語句
        # select * from tb_users where mobile=account or username=account;
        user_queryset = User.objects.filter(Q(mobile=account) | Q(username=account))
        if user_queryset:
            # 2. 校驗是否匹配'
            user = user_queryset.first()
            if user.check_password(password):
                # 3. 是否免密登陸
                if remember:
                    # 免密登陸14天
                    self.request.session.set_expiry(USER_SESSION_EXPIRY)
                else:
                    # 關閉瀏覽器就重置登陸狀態
                    self.request.session.set_expiry(0)
                # 4. 登錄
                login(self.request, user)
            else:
                raise forms.ValidationError('用戶名密碼錯誤!')

        else:
            raise forms.ValidationError('該賬戶不存在,請重新輸入!')

        return clean_data

在user文件加下創建constants.py文件定義如下常量

# 用戶session信息過期時間 單位秒 默認14天
USER_SESSION_EXPIRY = 14*24*60*60

4>前端代碼

修改user/login.html中用戶賬戶輸入框input的name爲account

在static/js/user/下創建login.js文件

js代碼如下:

$(function () {
    // 首先拿掉登錄按鈕原有的提交功能
    let $loginBtn = $('.login-btn');    // 獲取登錄按鈕元素
    $loginBtn.click(function (e) {
        e.preventDefault(); // 阻止默認提交
    // 其次校驗表單數據
        // 1.校驗賬戶
        let sAccount = $('input[name="account"]').val();
        if (sAccount === ''){
            message.showError('用戶賬戶不能爲空');
            return
        }
        if(!(/^\w{5,20}$/).test(sAccount) && !(/^1[3-9]\d{9}$/).test(sAccount)){
            message.showError('用戶賬戶格式不正確,請求重新輸入');
            return
        }
        // 2.校驗用戶輸入密碼
        let sPassword = $('input[name="password"]').val();
        if(sPassword === ''){
            message.showError('用戶密碼不能爲空');
            return
        }
        // 3.獲取用戶是否勾選'記住我',勾選爲true,否則爲false
        let bRemember = $('input[name="remember"]').is(':checked');
        
    // 然後發送ajax請求
        $.ajax({
            url: '/users/login/',
            data: {
                account: sAccount,
                password: sPassword,
                remember: bRemember
            },
            type: 'POST',
            dataType: 'json',
            success: function (res) {
                if(res.errno === '0'){
                    message.showSuccess('恭喜, 登錄成功!');
                    setTimeout(function () {
                        //註冊成功之後重定向到打開登錄頁面之前的頁面,document.referrer表示前一個的頁面
                        if(!document.referrer || document.referrer.includes('/users/login/') || document.referrer.includes('/users/register/')){
                            // 如果沒有前一個頁面(即直接通過登錄頁面的url訪問的),
                            // 或是前一個頁面就是登錄頁面(原地刷新),
                            // 或是前一個頁面是註冊頁面(先註冊後登陸),
                            // 那麼將直接跳轉到主頁面
                            window.location.href = '/'
                        }else {
                            // 如果是通過其他界面來到登錄的, 那就跳回前一個的頁面
                            window.location.href = document.referrer
                        }
                    }, 3000)
                }else{
                    message.showError(res.errmsg)
                }
            },
            error: function (xhr, msg) {
                message.showError('服務器超時,請重試')

            }
        });
    });
});

5>頁面效果

在這裏插入圖片描述

IIII. 登出功能

1>接口設計

接口說明:

類目 說明
請求方法 GET
url定義 /user/logout/
參數格式 無參數

2>後端代碼

# 在user目錄下的views.py文件中定義如下視圖:
class LogoutView(View):
    """
    登出視圖
    """
    def get(self, request):
        logout(request)
        
        return redirect(reverse('users:login'))
# 在urser目錄下的urls.py文件定義如下路由:
from django.urls import path
from . import views
app_name = 'users'

urlpatterns = [
    path('register/', views.RegisterView.as_view(), name='register'),
    path('login/', views.LoginView.as_view(), name='login'),
    path('logout/', views.LogoutView.as_view(), name='logout'),
]

3>前端代碼

修改templates/base/base.html中的header部分的代碼如下

通過is_authenticated確定當前登錄狀態, 然後if判斷,

如果已登錄則展示?‍?user.username, 如未登錄則展示登錄/註冊

<header id="header">
  <div class="mw1200 header-contain clearfix">
    <!-- logo start -->
    <h1 class="logo">
      <a href="javascript:void(0);" class="logo-title">Python</a>
    </h1>
    <!-- logo end -->
    <!-- nav start -->
    <nav class="nav">...</nav>
    <!-- nav end -->
    <!-- login start -->
      <div class="login-box">
          {% if user.is_authenticated %}
                <div class="author">
                    <i class="PyWhich py-user"></i>
                    <span>{{ user.username }}</span>
                    <ul class="author-menu">
                        <li><a href="javascript:void(0);">後臺管理</a></li>
                        <li><a href="{% url 'users:logout' %}">退出登錄</a></li>
                    </ul>
                </div>
            {% else %}
                <div>
                    <i class="PyWhich py-user"></i>
                    <span>
                  <a href="{% url 'users:login' %}" class="login">登錄</a> /
                  <a href="{% url 'users:register' %}" class="reg">註冊</a>
              </span>
                </div>
            {% endif %}
      </div>
    <!-- login end -->
  </div>
</header>

4>頁面效果:

在這裏插入圖片描述
項目源碼:https://gitee.com/hao4875/newssite

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章