用戶登錄登出功能
I. 功能需求分析
1>功能分析
1.1>流程圖
1.2>功能接口
- 登錄頁面
- 登錄功能
- 退出功能
II. 登陸頁面
1>接口設計
1.1>接口說明
類目 | 說明 |
---|---|
請求方法 | GET |
url定義 | /users/login/ |
參數格式 | 無參數 |
1.2>返回結果
登陸頁面
2.後端代碼
-
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')
-
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">立即註冊 ></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>業務流程分析
- 參數校驗
- 賬戶校驗(用戶名/手機號)
- 密碼校驗
- 賬戶密碼聯合校驗
- 登陸邏輯
- 登陸就是在session中保存狀態
- 根據
記住我
選項, 設置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>