咱們在開發一個網站的時候,無可避免的須要設計實現網站的用戶系統。此時咱們須要實現包括用戶註冊、用戶登陸、用戶認證、註銷、修改密碼等功能,這還真是個麻煩的事情呢。javascript
Django做爲一個完美主義者的終極框架,固然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據,使用auth模塊來進行用戶認證,那麼須要使用人家django自帶的auth_user表來存儲用戶的信息數據。html
模塊導入:前端
from django.contrib import auth
那麼有人就有疑問 了,這個auth_user表並非咱們本身在models.py文件中建立的啊,這經過代碼怎麼操做啊?java
其中一個往auth_user表裏面添加數據的命令,能夠先多添加幾個用戶,方便後面操做:python
python manage.py createsuperuser #要經過這個指令來建立用戶,由於這個指令會將你的密碼加密。
而後表中就有數據了:這個表裏面的數據如今先關注username和password字段就能夠了,其餘的字段可爲空。數據庫
內置的User模型擁有如下的字段: username: 用戶名。150個字符之內。能夠包含數字和英文字符,以及_、@、+、.和-字符。不能爲空,且必須惟一! first_name:歪果仁的first_name,在30個字符之內。能夠爲空。 last_name:歪果仁的last_name,在150個字符之內。能夠爲空。 email:郵箱。能夠爲空。 password:密碼。通過哈希事後的密碼。 #groups:分組。一個用戶能夠屬於多個分組,一個分組能夠擁有多個用戶。groups這個字段是跟Group的一個多對多的關係。 #user_permissions:權限。一個用戶能夠擁有多個權限,一個權限能夠被多個用戶全部用。和Permission屬於一種多對多的關係。 is_staff:是否能夠進入到admin的站點。表明是不是員工。這個字段若是不使用admin的話,能夠自行忽略,不影響使用 is_active:是不是可用的。對於一些想要刪除帳號的數據,咱們設置這個值爲False就能夠了,而不是真正的從數據庫中刪除。 is_superuser:是不是超級管理員。若是是超級管理員,那麼擁有整個網站的全部權限。 last_login:上次登陸的時間。 date_joined:帳號建立的時間。
auth 提供的兩種建立新用戶的方法,須要提供必要參數(username、password)等。django
普通用戶:create_user()後端
超級管理員:create_superuser()session
from django.contrib.auth.models import User user = User.objects.create_user(username='用戶名',password='密碼',email='郵箱',...) user_obj = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)
註冊代碼:app
# 註冊 def register(request): if request.method=="GET": return render(request,"register.html") else: username = request.POST.get("username") password = request.POST.get("password") # 判斷用戶名是否存在 if User.objects.filter(username=username).exists(): return HttpResponse("用戶名已經存在") # 註冊用戶 User.objects.create_user(username=username,password=password) return redirect("user_login")
提供了用戶認證功能,即驗證用戶名以及密碼是否正確,通常須要username 、password兩個關鍵字參數,由於你仔細看看auth_user表的話,你會發現用戶名和密碼的字段名稱就是username和password。
若是認證成功(用戶名和密碼正確有效,就是去auth_user表中查詢一下是否存在這條記錄),便會返回一個 User 對象,查詢認證失敗返回None。
authenticate()會在該 User 對象上設置一個屬性來標識後端已經認證了該用戶,且該信息在後續的登陸過程當中是須要的。
user = auth.authenticate(username='theuser',password='thepassword')
登陸代碼:
# 登陸 def user_login(request): if request.method=="GET": return render(request,"user_login.html") else: # 登陸驗證 username = request.POST.get("username") password = request.POST.get("password") # 判斷用戶名密碼是否正確,輸入正確返回含用戶信息的對象,輸入錯誤返回None user_obj = auth.authenticate(username=username,password=password) if user_obj: # 登陸成功設置session(幹了和session同樣的事情),request,user=user_obj(把查詢到的user_obj的model對象給了request.user) auth.login(request,user_obj) return redirect("home") else: # 登陸失敗重定向登陸界面 return redirect("user_login")
login(HttpRequest, user)
該函數接受一個HttpRequest對象,以及一個通過認證的User對象。
該函數實現一個用戶登陸的功能。它本質上會在後端爲該用戶生成相關session數據,保持會話用。
# 首頁 def home(request): print(request.user) # 是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號 print(request.user.id) # user表中的用戶id print(request.user.username) # 登陸用戶的名稱 print(request.user.is_active) # 登陸狀態 # 判斷當前是不是用戶登陸的 if request.user.is_authenticated(): return render(request,"home.html") else: return redirect("user_login")
logout(request)
該函數接受一個HttpRequest對象,無返回值。
當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。
其實內部就是執行了request.session.flush()
# 註銷 def logout(request): # 清空session auth.logout(request) return redirect("user_login")
check_password(raw_password)
auth 提供的一個檢查密碼是否正確的方法,須要提供當前請求用戶的密碼。
密碼正確返回True,不然返回False。
ok = user_obj.check_password('密碼')
或者直接針對當前請求的user對象校驗原密碼是否正確:
ok = request.user.check_password(raw_password='原密碼')
set_password(raw_password)
auth 提供的一個修改密碼的方法,接收 要設置的新密碼 做爲參數。
注意:設置完必定要調用用戶對象的save方法!!!
user_obj.set_password('新密碼') #user_obj其實就是request.user user_obj.save() #request.user.save()
修改代碼:
# 修改密碼 def set_password(request): if request.method=="GET": return render(request,"set_password.html") else: old_password = request.POST.get("old_password") new_password = request.POST.get("new_password") r_new_password = request.POST.get("r_new_password") print(new_password) print(r_new_password) # 判斷用戶輸入的舊密碼是否與數據庫中的一致 if request.user.check_password(old_password): if new_password != r_new_password: return HttpResponse("兩次輸入的密碼不一致") else: # 修改密碼 request.user.set_password(new_password) request.user.save() return redirect("user_login") else: return HttpResponse("舊密碼輸入有誤!!!")
用戶對象的屬性
user_obj可以拿到認證所用用戶表的數據屬性,好比username, password等。
其餘經常使用屬性含義以下:
is_staff : 用戶是否擁有網站的管理權限.
is_active : 是否容許用戶登陸, 設置爲 False,能夠在不刪除用戶的前提下禁止用戶登陸。
注意:
只要使用login(request, user_obj)以後,request.user就能拿到當前登陸的用戶對象。不然request.user獲得的是一個匿名用戶對象(AnonymousUser Object,是request.user的默認值),這個匿名用戶的狀態在個人視圖函數博客的那個request那一節有介紹,都是空。
詳細原理請查看 AuthenticationMiddleware 中間件源碼。
使用login方法以前,打印user的狀態
def index(request): print(request.user) #沒有通過login方法來封裝用戶的信息,那麼這個顯示的是一個匿名用戶 print(request.user.id) #None print(request.user.username) #空的 print(request.user.is_active) #False return render(request,'index.html')
使用login方法以後,打印user的狀態
def index(request): print(request.user) #chao,request.user對象自己是全局的,是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號 print(request.user.id) #1 #經過id、username等能夠判斷用戶是否登錄了,可是通常咱們都用後面要學的is_authenticated()方法來進行判斷。 print(request.user.username) #chao print(request.user.is_active) #True return render(request,'index.html')
from django.shortcuts import render,HttpResponse,redirect from django.contrib.auth.models import User from django.contrib import auth # Create your views here. # 註冊 def register(request): if request.method=="GET": return render(request,"register.html") else: username = request.POST.get("username") password = request.POST.get("password") # 判斷用戶名是否存在 if User.objects.filter(username=username).exists(): return HttpResponse("用戶名已經存在") # 註冊用戶 User.objects.create_user(username=username,password=password) return redirect("user_login") # 登陸 def user_login(request): if request.method=="GET": return render(request,"user_login.html") else: # 登陸驗證 username = request.POST.get("username") password = request.POST.get("password") # 判斷用戶名密碼是否正確,輸入正確返回含用戶信息的對象,輸入錯誤返回None user_obj = auth.authenticate(username=username,password=password) if user_obj: # 登陸成功設置session(幹了和session同樣的事情),request,user=user_obj(把查詢到的user_obj的model對象給了request.user) auth.login(request,user_obj) return redirect("home") else: # 登陸失敗重定向登陸界面 return redirect("user_login") # 首頁 def home(request): print(request.user) # 是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號 print(request.user.id) # user表中的用戶id print(request.user.username) # 登陸用戶的名稱 print(request.user.is_active) # 登陸狀態 # 判斷當前是不是用戶登陸的 if request.user.is_authenticated(): return render(request,"home.html") else: return redirect("user_login") # 註銷 def logout(request): # 清空session auth.logout(request) return redirect("user_login") # 修改密碼 def set_password(request): if request.method=="GET": return render(request,"set_password.html") else: old_password = request.POST.get("old_password") new_password = request.POST.get("new_password") r_new_password = request.POST.get("r_new_password") print(new_password) print(r_new_password) # 判斷用戶輸入的舊密碼是否與數據庫中的一致 if request.user.check_password(old_password): if new_password != r_new_password: return HttpResponse("兩次輸入的密碼不一致") else: # 修改密碼 request.user.set_password(new_password) request.user.save() return redirect("user_login") else: return HttpResponse("舊密碼輸入有誤!!!")
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>註冊用戶:</h1> <form action="{% url "register" %}" method="post"> {% csrf_token %} 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="註冊"> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用戶登陸:</h1> <form action="{% url "user_login" %}" method="post"> {% csrf_token %} 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="登陸"> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>當前登陸用戶:{{ request.user.username }}</h1> <a href="{% url "logout" %}">註銷</a> <a href="{% url "set_password" %}">修改密碼</a> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>修改密碼</h1> <form action="{% url "set_password" %}" method="post"> {% csrf_token %} 舊密碼:<input type="password" name="old_password"> 新密碼:<input type="password" name="new_password"> 確認密碼:<input type="password" name="r_new_password"> <input type="submit" value="提交"> </form> </body> </html>
user對象的 is_authenticated()
若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name
要求:
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:
django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞 當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。
auth 給咱們提供的一個裝飾器工具,用來快捷的給某個視圖添加登陸校驗。
用法:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' 並傳遞當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。
若是須要自定義登陸的URL,則須要在settings.py文件中經過LOGIN_URL進行修改。
示例:
LOGIN_URL = '/login/' # 這裏配置成你項目登陸頁面的路由
登陸成功以後跳轉頁面實列:
前端跳轉:
用戶直接請求修改密碼的界面,由於沒有登陸因此必須先登陸,而且會記錄用戶登陸成功後須要跳轉的下一個頁面,
取到/set_password/路徑,判斷若是取到路徑就跳轉,若是沒有就跳轉指定的路徑下(首頁)
後端跳轉:
這內置的認證系統這麼好用,可是auth_user表字段都是固定的那幾個,我在項目中無法拿來直接使用啊!
好比,我想要加一個存儲用戶手機號的字段,怎麼辦?
聰明的你可能會想到新建另一張表而後經過一對一和內置的auth_user表關聯,這樣雖然能知足要求可是有沒有更好的實現方式呢?
答案是固然有了。
咱們能夠經過繼承內置的 AbstractUser 類,來定義一個本身的Model類。django給咱們自動建立的一張user表,而若是要用auth模塊,就必需要使用(或繼承)這張表。
這樣既能根據項目需求靈活的設計用戶表,又能使用Django強大的認證系統了。繼承表的好處是咱們能夠增長一些本身須要的字段,而且同時可使用auth模塊提供的接口、方法
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用戶信息表 """ nid = models.AutoField(primary_key=True) phone = models.CharField(max_length=11, null=True, unique=True) def __str__(self): return self.username
須要注意的是,UserInfo表裏就不須要有auth_user裏重複的字段了,好比說username以及password等,可是仍是能夠直接使用這些字段的,而且django會自動將password進行加密
按上面的方式擴展了內置的auth_user表以後,必定要在settings.py中告訴Django,我如今使用我新定義的UserInfo表來作用戶認證。寫法以下:
# 引用Django自帶的User表,繼承使用時須要設置,這樣django就知道從咱們的app名的應用下的models文件中去查找UserInfo這張表了 AUTH_USER_MODEL = "app名.UserInfo"
自定義認證系統默認使用的數據表以後,咱們就能夠像使用默認的auth_user表那樣使用咱們的UserInfo表了。好比:
建立普通用戶:
UserInfo.objects.create_user(username='用戶名', password='密碼')
建立超級用戶:
UserInfo.objects.create_superuser(username='用戶名', password='密碼')
再次注意:
一旦咱們指定了新的認證系統所使用的表,咱們就須要從新在數據庫中建立該表,而不能繼續直接使用原來默認的auth_user表了。