小白學Python-使用Django實現商城登陸功能

用戶名登陸

登陸的核心思想,認證和狀態保持,經過用戶的認證,肯定該登陸用戶是美多商場的註冊用戶。經過狀態保持緩存用戶的惟一標識信息,用於後續是否登陸的判斷。html

1. 用戶名登陸邏輯分析

image-20210601224743047

2. 用戶名登陸接口設計

1.請求方式
選項 方案
請求方法 POST
請求地址 /login/
2.請求參數:表單
參數名 類型 是否必傳 說明
username string 用戶名
password string 密碼
remembered string 是否記住用戶
3.響應結果:HTML
字段 說明
登陸失敗 響應錯誤提示
登陸成功 重定向到首頁

3. 用戶名登陸接口定義

class LoginView(View):
    """用戶名登陸"""

    def get(self, request):
        """
        提供登陸界面
        :param request: 請求對象
        :return: 登陸界面
        """
        pass

    def post(self, request):
        """
        實現登陸邏輯
        :param request: 請求對象
        :return: 登陸結果
        """
        pass

4. 用戶名登陸後端邏輯

class LoginView(View):
    """用戶名登陸"""

    def get(self, request):
        """
        提供登陸界面
        :param request: 請求對象
        :return: 登陸界面
        """
        return render(request, 'login.html')

    def post(self, request):
        """
        實現登陸邏輯
        :param request: 請求對象
        :return: 登陸結果
        """
        # 接受參數
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')

        # 校驗參數
        # 判斷參數是否齊全
        if not all([username, password]):
            return http.HttpResponseForbidden('缺乏必傳參數')

        # 判斷用戶名是不是5-20個字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('請輸入正確的用戶名或手機號')

        # 判斷密碼是不是8-20個數字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('密碼最少8位,最長20位')

        # 認證登陸用戶
        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用戶名或密碼錯誤'})

        # 實現狀態保持
        login(request, user)
        # 設置狀態保持的週期
        if remembered != 'on':
            # 沒有記住用戶:瀏覽器會話結束就過時
            request.session.set_expiry(0)
        else:
            # 記住用戶:None表示兩週後過時
            request.session.set_expiry(None)

        # 響應登陸結果
        return redirect(reverse('contents:index'))

多帳號登陸

Django自帶的用戶認證系統只會使用用戶名去認證一個用戶。因此咱們爲了實現多帳號登陸,用戶名、手機號或者第三方登錄,就須要自定義認證後端,採用其餘的惟一信息去認證一個用戶python

自定義用戶認證後端步驟ajax

  • 在users應用中新建utils.py文件
  • 新建類,繼承自ModelBackend
  • 重寫認證authenticate()方法
  • 分別使用用戶名和手機號查詢用戶
  • 返回查詢到的用戶實例

1. 自定義用戶認證後端

users.utils.py
from django.contrib.auth.backends import ModelBackend
import re
from .models import User


def get_user_by_account(account):
    """
    根據account查詢用戶
    :param account: 用戶名或者手機號
    :return: user
    """
    try:
        if re.match('^1[3-9]\d{9}$', account):
            # 手機號登陸
            user = User.objects.get(mobile=account)
        else:
            # 用戶名登陸
            user = User.objects.get(username=account)
    except User.DoesNotExist:
        return None
    else:
        return user


class UsernameMobileAuthBackend(ModelBackend):
    """自定義用戶認證後端"""

    def authenticate(self, request, username=None, password=None, **kwargs):
        """
        重寫認證方法,實現多帳號登陸
        :param request: 請求對象
        :param username: 用戶名
        :param password: 密碼
        :param kwargs: 其餘參數
        :return: user
        """
        # 根據傳入的username獲取user對象。username能夠是手機號也能夠是帳號
        user = get_user_by_account(username)
        # 校驗user是否存在並校驗密碼是否正確
        if user and user.check_password(password):
            return user

2. 配置自定義用戶認證後端

1.Django自帶認證後端源碼

image-20210601224911621

2.配置自定義用戶認證後端
# 指定自定義的用戶認證後端
AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']

3. 測試自定義用戶認證後端

image-20210601224932855

首頁用戶名展現

image-20210601225115949

1. 首頁用戶名展現方案

方案一django

  • 模板中 request 變量直接渲染用戶名
  • 缺點:不方便作首頁靜態化
{% if user.is_authenticated %}
    <div class="login_btn fl">
        歡迎您:<em>{{ user.username }}</em>
        <span>|</span>
        <a href="#">退出</a>
    </div>
    {% else %}
    <div class="login_btn fl">
        <a href="login.html">登陸</a>
        <span>|</span>
        <a href="register.html">註冊</a>
    </div>
{% endif %}

方案二後端

  • 發送ajax請求獲取用戶信息
  • 缺點:須要發送網絡請求
<div class="login_btn fl">
    {# ajax渲染 #}
</div>

方案三瀏覽器

  • Vue讀取cookie渲染用戶信息
<div v-if="username" class="login_btn fl">
    歡迎您:<em>[[ username ]]</em>
    <span>|</span>
    <a href="#">退出</a>
</div>
<div v-else class="login_btn fl">
    <a href="login.html">登陸</a>
    <span>|</span>
    <a href="register.html">註冊</a>
</div>

結論:緩存

  • 對比此三個方案,咱們在本項目中選擇 方案三

實現步驟:cookie

  • 註冊或登陸後,用戶名寫入到cookie
  • Vue渲染主頁用戶名

2. 用戶名寫入到cookie

# 響應註冊結果
response = redirect(reverse('contents:index'))

# 註冊時用戶名寫入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

return response
# 響應登陸結果
response = redirect(reverse('contents:index'))

# 登陸時用戶名寫入到cookie,有效期15天
response.set_cookie('username', user.username, max_age=3600 * 24 * 15)

return response

3. Vue渲染首頁用戶名

1.index.html
<div v-if="username" class="login_btn fl">
    歡迎您:<em>[[ username ]]</em>
    <span>|</span>
    <a href="#">退出</a>
</div>
<div v-else class="login_btn fl">
    <a href="login.html">登陸</a>
    <span>|</span>
    <a href="register.html">註冊</a>
</div>
2.index.js
mounted(){
    // 獲取cookie中的用戶名
    this.username = getCookie('username');
},

退出登陸

退出登陸的核心思想就是清理登陸時緩存的狀態保持信息。因爲首頁中用戶名是從cookie中讀取的。因此退出登陸時,須要將cookie中用戶名清除。網絡

1. logout()方法介紹

  1. 退出登陸:session

    • 回顧登陸:將經過認證的用戶的惟一標識信息,寫入到當前session會話中
    • 退出登陸:正好和登陸相反(清理session會話信息)
  2. logout()方法:

    • Django用戶認證系統提供了logout()方法
    • 封裝了清理session的操做,幫助咱們快速實現登出一個用戶
  3. logout()位置:

    • django.contrib.auth.__init__.py文件中
logout(request)

2. logout()方法使用

class LogoutView(View):
    """退出登陸"""

    def get(self, request):
        """實現退出登陸邏輯"""
        # 清理session
        logout(request)
        # 退出登陸,重定向到登陸頁
        response = redirect(reverse('contents:index'))
        # 退出登陸時清除cookie中的username
        response.delete_cookie('username')

        return response

判斷用戶是否登陸

1. 展現用戶中心界面

class UserInfoView(View):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        return render(request, 'user_center_info.html')

需求:

  • 當用戶登陸後,才能訪問用戶中心。
  • 若是用戶未登陸,就不容許訪問用戶中心,將用戶引導到登陸界面。

實現方案:

  • 須要判斷用戶是否登陸。
  • 根據是否登陸的結果,決定用戶是否能夠訪問用戶中心。

2. is_authenticate 判斷用戶是否登陸

介紹:

  • Django用戶認證系統提供了方法request.user.is_authenticated()來判斷用戶是否登陸。
  • 若是經過登陸驗證則返回True。反之,返回False
  • 缺點:登陸驗證邏輯不少地方都須要,因此該代碼須要重複編碼好屢次。
class UserInfoView(View):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        if request.user.is_authenticated():
            return render(request, 'user_center_info.html')
        else:
            return redirect(reverse('users:login'))

3. login_required裝飾器 判斷用戶是否登陸

  • Django用戶認證系統提供了裝飾器

    login_required

來判斷用戶是否登陸。

  • 內部封裝了is_authenticate
  • 位置:django.contrib.auth.decorators
  • 若是經過登陸驗證則進入到視圖內部,執行視圖邏輯。
  • 若是未經過登陸驗證則被重定向到

    LOGIN_URL

配置項指定的地址。

  • 以下配置:表示當用戶未經過登陸驗證時,將用戶重定向到登陸頁面。

    LOGIN_URL = '/login/'

1.裝飾as_view()方法返回值

提示:

  • login_required裝飾器能夠直接裝飾函數視圖,可是本項目使用的是類視圖。
  • as_view()方法的返回值就是將類視圖轉成的函數視圖。

結論:

  • 要想使用login_required裝飾器裝飾類視圖,能夠間接的裝飾as_view()方法的返回值,以達到預期效果。
url(r'^info/$', login_required(views.UserInfoView.as_view()), name='info'),
class UserInfoView(View):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        return render(request, 'user_center_info.html')

2.定義View子類封裝login_required裝飾器

  • 提示:LoginRequired(object)依賴於視圖類View,複用性不好。
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(View):
  """驗證用戶是否登錄"""

  @classmethod
  def as_view(cls, **initkwargs):
      # 自定義as_view()方法中,調用父類的as_view()方法
      view = super().as_view()
      return login_required(view)


class UserInfoView(LoginRequired):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        return render(request, 'user_center_info.html')

3.定義obejct子類封裝login_required裝飾器

  • 提示:LoginRequired(object)不依賴於任何視圖類,複用性更強。
url(r'^info/$', views.UserInfoView.as_view(), name='info'),
class LoginRequired(object):
  """驗證用戶是否登錄"""

  @classmethod
  def as_view(cls, **initkwargs):
      # 自定義as_view()方法中,調用父類的as_view()方法
      view = super().as_view()
      return login_required(view)


class UserInfoView(LoginRequired, View):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        return render(request, 'user_center_info.html')

4.定義驗證用戶是否登陸擴展類

  • 提示:定義擴展類方便項目中導入和使用(meiduo_mall.utils.views.py)
class LoginRequiredMixin(object):
  """驗證用戶是否登陸擴展類"""

  @classmethod
  def as_view(cls, **initkwargs):
      # 自定義的as_view()方法中,調用父類的as_view()方法
      view = super().as_view()
      return login_required(view)
 class UserInfoView(LoginRequiredMixin, View):
    """用戶中心"""

    def get(self, request):
        """提供我的信息界面"""
        return render(request, 'user_center_info.html')

4. 登陸時next參數的使用

1.next參數的效果
http://127.0.0.1:8000/login/?next=/info/

2.next參數的做用

  • 由Django用戶認證系統提供,搭配login_required裝飾器使用。
  • 記錄了用戶未登陸時訪問的地址信息,能夠幫助咱們實如今用戶登陸成功後直接進入未登陸時訪問的地址。
# 響應登陸結果
next = request.GET.get('next')
if next:
    response = redirect(next)
else:
    response = redirect(reverse('contents:index'))
相關文章
相關標籤/搜索