注冊功能實現(xiàn) -- 8.用戶登錄登出功能

一、用戶登錄功能實現(xiàn)

1.分析

業(yè)務(wù)處理流程:

  • 判斷用戶輸入的賬號是否為空
  • 判斷用戶輸入的密碼是否為空,格式是否正確
  • 判斷用戶輸入的賬號與密碼是否正確

請求方法POST

url定義/users/login/

請求參數(shù):url路徑參數(shù)

參數(shù) 類型 前端是否必須傳 描述
user_account 字符串 用戶輸入的賬號可以是手機(jī)號也可以是用戶名
password 字符串 用戶輸入的密碼
remember_me 字符串 用戶輸入的“是否記住我”

注:由于是post請求,在向后端發(fā)起請求時,需要附帶csrf token

2.后端代碼實現(xiàn)

# views.py
import json
import logging

from django.views import View
from django.shortcuts import render
from django.contrib.auth import login

from .models import Users
from .forms import RegisterForms, LoginForm
from utils.res_code.rescode import Code, error_map
from utils.json_translate.json_fun import to_json_data


logger = logging.getLogger('django')



class LoginView(View):
    """

    """
    def get(self, request):
        """
        渲染登陸界面
        :param request:
        :return:
        """
        return render(request, 'users/login.html')

    def post(self, request):
        """
        1.創(chuàng)建一個類視圖  LoginView
        2.明確請求方式:
            -- 請求類型: Ajax post
            -- 傳參方式: 請求體  user_account  password  remember_me
            -- url定義: '/users/login/'
        3.獲取前端參數(shù):
            -- json_data = request.body
        4.業(yè)務(wù)處理:
            -- 1.是否需要校驗              -- 需要  form表單校驗
            -- 2.是否需要儲存以及儲存方式   -- 不需要
            -- 3.其他:
                -- form表單登陸
        5.返回前端數(shù)據(jù):
            -- to_json_data(errorno= , errormsg= , data= )
        :param request:
        :return:
        """
        json_data = request.body

        if not json_data:
            return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])

        dict_data = json.loads(json_data.decode('utf-8'))

        form = LoginForm(data=dict_data, request=request)

        if form.is_valid():
            return to_json_data(errno=Code.OK, errmsg='恭喜你登陸成功')
        else:
            # 定義一個接收錯誤信息的列表
            form_errormsg_list = []

            for msg in form.errors.get_json_data().values():
                form_errormsg_list.append(msg[0].get('message'))
            form_errormsg = '/'.join(form_errormsg_list)

            return to_json_data(errno=Code.PARAMERR, errmsg=form_errormsg)
# urls.py

from django.urls import path
from . import views

app_name = 'users'

urlpatterns = [
    path('login/', views.LoginView.as_view(), name='user_login'),
    path('register/', views.RegisterView.as_view(), name='user_register'),
]
# forms.py
import re

from django import forms
from django.db.models import Q
from django.contrib.auth import login
from django_redis import get_redis_connection
from django.core.validators import RegexValidator

from .models import Users
from .constants import USER_SESSION_EXPIRES
from verifications.constants import SMS_CODE_NUMS

password_Validator=RegexValidator(r'^\w{6,20}$', '密碼格式不正確,請重新輸入')

class LoginForm(forms.Form):
    """
    獲取參數(shù)
        -- user_account(username/mobile) password remember_me
    業(yè)務(wù)邏輯
        -- user_account password 聯(lián)合登陸
        -- 實現(xiàn)自動登陸(session)
    """
    user_account = forms.CharField()
    password = forms.CharField(max_length=20, min_length=6, label='密碼', validators=[password_Validator,],
                               error_messages={
                                   'max_length':'密碼長度要小于20',
                                   'min_length':'密碼長度要大于6',
                                   'required':'密碼不能為空',
                               })
    remember_me = forms.BooleanField(required=False)

    # 本項目在form表單里實現(xiàn)用戶登陸,須將login的request參數(shù)傳入
    # 需要__init__ 導(dǎo)入視圖當(dāng)中的request post(self, request
    def __init__(self, *args, **kwargs):  # *args接收request  **kwargs接收request=request  *args 沒有用到也需要加 固定用法
        # 定義實例屬性
        self.request = kwargs.pop('request', None)   # 字典方法pop() 有key則返回 value 無則返回 None
        # 繼承父類
        super().__init__(*args, **kwargs)


    def clean_user_account(self):
        cleaned_user_account = self.cleaned_data.get('user_account')
        if not cleaned_user_account:
            raise forms.ValidationError('用戶賬號名不能為空')
        if not re.match(r'^\w{5,20}$', cleaned_user_account) and not re.match(r'^1[3-9]\d{9}$', cleaned_user_account):
            raise forms.ValidationError('用戶賬號不存在')

        return cleaned_user_account

    def clean(self):
        cleaned_data = super().clean()
        cleaned_user_account = cleaned_data.get('user_account')
        cleaned_password = cleaned_data.get('password')
        cleaned_remember_me = cleaned_data.get('remember_me')

        try:
            user = Users.objects.filter(Q(username=cleaned_user_account)|Q(mobile=cleaned_user_account))
        except Exception as e:
            raise forms.ValidationError('內(nèi)部錯誤')

        if user:
            user = user.first()
            if user.check_password(cleaned_password):
                if cleaned_remember_me:
                    self.request.session.set_expiry(USER_SESSION_EXPIRES)
                else:
                    self.request.session.set_expiry(0) # None--默認(rèn)兩周 0--默認(rèn)關(guān)閉瀏覽器就刪除
                # 登陸  {% if user.is_authenticated %} 此處user傳入,可以將其渲染到模板中  因為它:'django.contrib.auth.context_processors.auth',
                login(self.request, user)
            else:
                raise forms.ValidationError('密碼輸入不正確')
        else:
            raise forms.ValidationError('用戶賬號不存在')
# 在users目錄下的constants.py文件中定義如下常量:

# 用戶session信息過期時間,單位秒,這是設(shè)置為5天
USER_SESSION_EXPIRES = 5 * 24 * 60 * 60

3.前端代碼實現(xiàn)

# 在static/js/users中創(chuàng)建一個login.js文件

$(function () {
  let $login = $('.form-contain');  // 獲取登錄表單元素

  // for test
  // console.log(document.referrer);   // 將referrer url 打印到終端

  // 登錄邏輯
  $login.submit(function (e) {
    // 阻止默認(rèn)提交操作
    e.preventDefault();

    // 獲取用戶輸入的賬號信息
    let sUserAccount = $("input[name=telephone]").val();  // 獲取用戶輸入的用戶名或者手機(jī)號
    // 判斷用戶輸入的賬號信息是否為空
    if (sUserAccount === "") {
      message.showError('用戶賬號不能為空');
      return
    }
    // 判斷輸入手機(jī)號格式或者用戶名格式是否正確
    if (!(/^1[3-9]\d{9}$/).test(sUserAccount) && !(/^\w{5,20}$/).test(sUserAccount)) {
      message.showError('請輸入合法的用戶賬號:5-20個字符的用戶名或者11位手機(jī)號');
      return
    }

    // 獲取用戶輸入的密碼
    let sPassword = $("input[name=password]").val();  // 獲取用戶輸入的密碼
    // 判斷用戶輸入的密碼是否為空
    if (!sPassword) {
      message.showError('密碼不能為空');
      return
    }
    // 判斷用戶輸入的密碼是否為6-20位
    if (sPassword.length < 6 || sPassword.length > 20) {
      message.showError('密碼的長度需在6~20位以內(nèi)');
      return
    }

    // 獲取用戶是否勾許"記住我",勾許為true,不勾許為false
    let bStatus = $("input[type='checkbox']").is(":checked");  // 獲取用戶是否選擇記住我,勾上代表true,沒勾上代碼false

    // 發(fā)起登錄請求
    // 創(chuàng)建請求參數(shù)
    let SdataParams = {
      "user_account": sUserAccount,
      "password": sPassword,
      "remember_me": bStatus
    };

    // 創(chuàng)建ajax請求
    $.ajax({
      // 請求地址
      url: "/users/login/",  // url尾部需要添加/
      // 請求方式
      type: "POST",
      data: JSON.stringify(SdataParams),
      // 請求內(nèi)容的數(shù)據(jù)類型(前端發(fā)給后端的格式)
      contentType: "application/json; charset=utf-8",
      // 響應(yīng)數(shù)據(jù)的格式(后端返回給前端的格式)
      dataType: "json"
    })
      .done(function (res) {
        if (res.errno === "200") {
          // 注冊成功
          message.showSuccess('恭喜你,登錄成功!');
          setTimeout(function () {
            // 注冊成功之后重定向到打開登錄頁面之前的頁面
            window.location.href = document.referrer;
          }, 1000)
        } else {
          // 登錄失敗,打印錯誤信息
          message.showError(res.errmsg);
        }
      })
      .fail(function(){
        message.showError('服務(wù)器超時,請重試!');
      });
  });

  // get cookie using jQuery
  function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      let cookies = document.cookie.split(';');
      for (let i = 0; i < cookies.length; i++) {
        let cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) === (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  // Setting the token on the AJAX request
  $.ajaxSetup({
    beforeSend: function (xhr, settings) {
      if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
      }
    }
  });

});
// login.html
{% block script %}
  <script src="{% static 'js/users/login.js' %}"></script>
  <script src="{% static 'js/base/message.js' %}"></script>
{% endblock %}

二、用戶登出功能實現(xiàn)

1.分析

請求方法GET

url定義/users/logout/

實現(xiàn):調(diào)用Django自帶的logout(request)函數(shù)即可

2.后端代碼實現(xiàn)

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

class LogoutView(View):
    """
    """
    def get(self, request):
        logout(request)

        return redirect(reverse("users:user_login"))
# 在users目錄下的urls.py文件中定義如下路由:

from django.urls import path
from . import views

app_name = 'users'

urlpatterns = [
    path('logout/', views.LogoutView.as_view(), name='user_logout'),

]

3.前端代碼實現(xiàn)

# 在templates/base下的base.html中修改如下代碼:

<!-- login start -->
    <div class="login-box">
      <!-- is_authenticated request -->
      {% if user.is_authenticated %}
        <div class="author">
          <i class="PyWhich py-user"></i>
          <span>{{ user.username }}</span>
          <ul class="author-menu">

            {% if user.is_staff %}
              <li><a href="#">后臺管理</a></li>
            {% endif %}

            <li><a href="{% url 'users:user_logout' %}">退出登錄</a></li>
          </ul>
        </div>
      {% else %}
        <div>
          <i class="PyWhich py-user"></i>
          <span>
            <a href="{% url 'users:user_login' %}" class="login">登錄</a> / <a href="{% url 'users:user_register' %}" class="reg">注冊</a>
          </span>
        </div>
      {% endif %}
      <!-- login end -->
    </div>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容