登陸的核心思想,認證和狀態保持,經過用戶的認證,肯定該登陸用戶是美多商場的註冊用戶。經過狀態保持緩存用戶的惟一標識信息,用於後續是否登陸的判斷。html
1.請求方式
選項 | 方案 |
---|---|
請求方法 | POST |
請求地址 | /login/ |
2.請求參數:表單
參數名 | 類型 | 是否必傳 | 說明 |
---|---|---|---|
username | string | 是 | 用戶名 |
password | string | 是 | 密碼 |
remembered | string | 是 | 是否記住用戶 |
3.響應結果:HTML
字段 | 說明 |
---|---|
登陸失敗 | 響應錯誤提示 |
登陸成功 | 重定向到首頁 |
class LoginView(View): """用戶名登陸""" def get(self, request): """ 提供登陸界面 :param request: 請求對象 :return: 登陸界面 """ pass def post(self, request): """ 實現登陸邏輯 :param request: 請求對象 :return: 登陸結果 """ pass
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
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
1.Django自帶認證後端源碼
2.配置自定義用戶認證後端
# 指定自定義的用戶認證後端 AUTHENTICATION_BACKENDS = ['users.utils.UsernameMobileAuthBackend']
方案一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渲染主頁用戶名
# 響應註冊結果 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
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中用戶名清除。網絡
退出登陸:session
logout()方法:
logout()
方法logout()位置:
django.contrib.auth.__init__.py
文件中logout(request)
class LogoutView(View): """退出登陸""" def get(self, request): """實現退出登陸邏輯""" # 清理session logout(request) # 退出登陸,重定向到登陸頁 response = redirect(reverse('contents:index')) # 退出登陸時清除cookie中的username response.delete_cookie('username') return response
class UserInfoView(View): """用戶中心""" def get(self, request): """提供我的信息界面""" return render(request, 'user_center_info.html')
需求:
- 當用戶登陸後,才能訪問用戶中心。
- 若是用戶未登陸,就不容許訪問用戶中心,將用戶引導到登陸界面。
實現方案:
- 須要判斷用戶是否登陸。
- 根據是否登陸的結果,決定用戶是否能夠訪問用戶中心。
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'))
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')
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'))