一、自己實現登錄驗證
二、Django自帶的用戶驗證模塊——auth
三、自己動手解決auth不足的地方
一、自己實現登錄驗證
需求:一共有index和login兩個頁面,如果用戶沒有登錄訪問index頁面,則會自動跳轉到login頁面進行登錄,用戶在login登錄之后,會跳轉到index頁面,頁面出現歡迎用戶字樣
分析:數據庫和session相結合,可以實現不同用戶顯示不同歡迎字樣。不過自己寫裝飾器太多麻煩,而且如果用戶多的話session的驗證重復步驟就比較多
1.寫入函數
#views.py
from functools import wraps
def check_login(f):
@wraps(f) #取消裝飾器裝飾完成之后,函數名稱改變的問題
def inner(request, *args, **kwargs):
if request.session.get("is_login") == "1": # 如果session中(is_login)對應的value為1,就執行f()函數,否則,返回登錄頁面
return f(request, *args, **kwargs)
else:
return redirect("/login/")
return inner
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password") #從html中拿到用戶輸入的賬號和密碼
user = models.User.objects.filter(username=username, password=password) # 從數據庫中拿到賬號和密碼和用戶輸入一致的數據
if user: #如果有,則證明用戶賬號密碼正確,如果沒有,就返回登錄頁面
# 登陸成功
request.session["is_login"] = "1" #為這次登錄設置一個session,key為is_login,value為'1'
# request.session["username"] = username
request.session["user_id"] = user[0].id # 為這次登錄設置一個session,key為username,value為數據庫中本賬號密碼保存的id
# 寫上面的一條代碼,django后台自動做的事情
# 1. 生成特殊的字符串
# 2. 特殊字符串當成key,在數據庫的session表中對應一個session value
# 3. 在響應中向瀏覽器寫了一個Cookie Cookie的值就是 特殊的字符串
return redirect("/index/")
return render(request, "login.html")
@check_login #裝飾器,檢測用戶是否登錄,如果登錄,就執行index函數,如果沒有,就跳轉到登錄頁面
def index(request):
user_id = request.session.get("user_id")
# 根據id去數據庫中查找用戶
user_obj = models.User.objects.filter(id=user_id) # 設置session的時候,把數據庫中的id設為了session中的key,目的就是在這里再次得到id,從數據庫中拿出其它數據
if user_obj:
return render(request, "index.html", {"user": user_obj[0]})
else:
return render(request, "index.html", {"user": "匿名用戶"})
2.補充index.html頁面
# index.html
<!DOCTYPE html>
<html>
<head>
<title>index</title>
</head>
<body>
<h3>This is a test page!</h3>
hello,{{ user.username }}! # 替換登錄的名字
</body>
</html>
3.創建數據表
# models.py
class User(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=20, null=False)
password = models.CharField(max_length=20, null=False)
二、Django自帶的用戶驗證模塊——auth
在使用這個被前輩們封裝好的模塊之前,我們還是先來學習一下模塊的基本使用方法吧!
1.創建超級用戶
python manage.py createsuperuser
# 超級用戶數據表(auth_user)是django自動幫忙創建的,但是不可以直接往里面寫入數據哦!
# 因為直接寫入的話,密碼是明文顯示的,而一般來說,密碼需要以加密形式保存到數據庫中
# 如:username='username', password='$MqFtX/a3inUsPdJekYDMh8H4ZkohfCl3Lc4Vj5jZuNI='
2.authenticate()
from django.contrib import auth #導入auth模塊
# 驗證用戶名和密碼,如果驗證成功,得到的是一個用戶對象,如果驗證失敗,得到的是匿名用戶,取它的任意字段都是空
auth.authenticate(username='theuser',password='thepassword')
如果認證信息有效,會返回一個 User 對象。authenticate()會在User 對象上設置一個屬性來標識后端已經認證了該用戶,且該信息在后續的登錄過程中是需要的。
3.login(HttpRequest, user)
auth.login(request, user)
# 將驗證的用戶注入request.user屬性
# 該函數接受一個HttpRequest對象,以及一個認證了的User對象
# 此函數使用django的session框架給某個已認證的用戶附加上session id等信息。
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
4.logout(request) 注銷用戶
# 該函數接受一個HttpRequest對象,無返回值。
# 當調用該函數時,當前請求的session信息會全部清除。
# 該用戶即使沒有登錄,使用該函數也不會報錯。
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
5.user對象的 is_authenticated()
要求:
1 用戶登陸后才能訪問某些頁面,
2 如果用戶沒有登錄就訪問該頁面的話直接跳到登錄頁面
3 用戶在跳轉的登陸界面中完成登陸后,自動訪問跳轉到之前訪問的地址
方法1:
def my_view(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
方法2:
from django.contrib.auth.decorators import login_required
@login_required # django已經為我們設計好了一個用於此種情況的裝飾器:login_requierd()
def my_view(request):
...
# 若用戶沒有登錄,則會跳轉到django默認的登錄URL '/accounts/login/ '
# 默認url可以在settings.py文件中通過LOGIN_URL進行修改(
在settings.py中加入 LOGIN_URL = '/login/')
# 登陸成功后,會重定向到該路徑
注意:
1. 如果是真正的 User 對象,返回值恆為 True 。 用於檢查用戶是否已經通過了認證。通過認證並不意味着用戶擁有任何權限,這個方法甚至也不檢查該用戶是否處於激活狀態,只是表明用戶成功的通過了認證。
2. 這個方法很重要, 在后台用request.user.is_authenticated()判斷用戶是否已經登錄,如果true則可以向前台展示request.user.name
6.使用create_user輔助函數創建用戶
def register(request):
from django.contrib.auth.models import User
user = User.objects.create_user(username='abcd', password='999999999') # 這里把它寫死了,實際中要從頁面中post過來,得到用戶輸入的賬號和密碼
# user = User.objects.create_user(username='',password='',email='') # email可以不寫,password至少8個字符,password用哈希算法保存到數據庫
# 這里創建用戶一共有三種:1. create() 密碼明文保存; 2. create_superuser() 創建超級用戶 3. create_user() 創建普通用戶
return HttpResponse('successful!')
7.使用check_password(passwd)檢查密碼
def register(request):
from django.contrib.auth.models import User
user_obj = User.objects.create_user(username='abcd', password='999999999')
user_obj.check_password('888888888') # --->返回false
user_obj.check_password('999999999') # --->返回true,為了演示,這里同樣也把數值給寫死了
8.使用 set_password() 來修改密碼
user = User.objects.get(username='') # 得到要修改密碼的對象
user.set_password(password='') # 重新設置密碼
user.save() # 保存
三、auth_user字段不夠的解決方案
1.一對一表
from django.contrib.auth.models import User
class UserDetail(models.Model):
phone = models.CharField(max_length=11)
user = models.OneToOneField(to=User) #這里關聯的User就是上面導入的User
2.類的繼承(繼承auth_user表)
from django.contrib.auth.models import User,
AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length=11)
# 如果使用繼承的方式,需要在settings.py中配置 默認用戶認證時使用的是哪張表
# AUTH_USER_MODEL = 'app01.UserInfo'
# 另外使用這種方法之前,要把auth自己創建的user表刪除才可以
# 作用:替代auth_user並擴展功能
四、未處理的兩點內容
is_staff : 用戶是否擁有網站的管理權限.
is_active : 是否允許用戶登錄, 設置為``False``,可以不用刪除用戶來禁止 用戶登錄
簡單示例
def sign_up(request):
state = None
if request.method == 'POST':
password = request.POST.get('password', '')
repeat_password = request.POST.get('repeat_password', '')
email=request.POST.get('email', '')
username = request.POST.get('username', '')
if User.objects.filter(username=username):
state = 'user_exist'
else:
new_user = User.objects.create_user(username=username, password=password,email=email)
new_user.save()
return redirect('/book/')
content = {
'state': state,
'user': None,
}
return render(request, 'sign_up.html', content)
注冊示例代碼
@login_required
def set_password(request):
user = request.user
state = None
if request.method == 'POST':
old_password = request.POST.get('old_password', '')
new_password = request.POST.get('new_password', '')
repeat_password = request.POST.get('repeat_password', '')
if user.check_password(old_password):
if not new_password:
state = 'empty'
elif new_password != repeat_password:
state = 'repeat_error'
else:
user.set_password(new_password)
user.save()
return redirect("/log_in/")
else:
state = 'password_error'
content = {
'user': user,
'state': state,
}
return render(request, 'set_password.html', content)
修改密碼示例