用戶認證組件
用戶認證組件:
功能:用session記錄登陸驗證狀態
前提:用戶表:django自帶的auth_user
創建超級用戶的命令: python manage.py createsuperuser
API:
(1)from django.contrib import auth (auth模塊的方法)
1. # 做驗證:如果 驗證成功 返回 user 對象(就是 auth_user 這張表中的一條記錄對象),否則返回None
user = auth.authenticate(username=username,password=psw)
2. # 利用 session 信息注冊一個用戶登陸對象(在 django_session表中添加記錄,並生成一個全局變量 request.user這個對象)
auth.login(request,user)
3. # 注銷
auth.logout(request)
(2)from django.contrib.auth.models import User (User對象的方法;User等同於 auth_user這張表)
4. request.user.is_authenticated # 是否通過驗證 # request.user 就是 auth_user表中的一條記錄(一個對象)
5. 添加用戶(注冊功能)
User.objects.create_user(username="",password="",...)
User.objects.create_superuser(username="",password="",...)
補充:
匿名用戶對象:見下面
重點: request.user 這個全局變量(可在任何視圖和模板中直接使用;這也是auth認證組件最大的優點:可利用 request.user 獲取當前登陸人的所有信息)
如果沒有執行 auth.login(request,user),那么 request.user == AnonymousUser()
如果執行了 auth.login(request,user),request.user == 當前用戶對象
目錄結構:
urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path(r'login/',views.login), path(r'index/',views.index), # 注銷功能 path(r'logout/',views.logout), # 注冊功能 path(r'reg/',views.reg), # 認證裝飾器 path(r"order/",views.order) ]
views.py
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from django.contrib import auth # 引入 auth_user表 from django.contrib.auth.models import User def login(request): if request.method == "POST": # 用 auth 模塊注冊 session username = request.POST.get("username") psw = request.POST.get("psw") # 如果 驗證成功 返回 user 對象(就是 auth_user 這張表中的一條記錄對象),否則返回None user = auth.authenticate(username=username,password=psw) # 利用 auth.authenticate()這個接口去 auth_user 表中進行驗證;返回一條記錄對象 if user: auth.login(request,user) # 利用 auth.login(request,user) 這個接口去注冊 session (把 user這個對象添加到了 django_session這個表的 session_data 中);這行代碼執行的操作是: request.user = user;即 request 中添加了一個屬性 user,其值為 user這個對象,以后在全局中都能通過 request.user 獲取到 user 對象; request.user是個全局變量,不管是在視圖函數中還是模板中都能直接調用 # 如果沒有進行 auth.login(),那就會以 匿名用戶(AnonymousUser)的身份進行登陸 # 通過 auth 進行session注冊的好處:邏輯更嚴謹;例如,當 用戶登陸信息更新時, django_session表中 不但 session_data 會更新,而且session_key也會更新為一個新的隨機字符串 return redirect("/index/") # 如果所有需要 is_authenticated為True的函數都加了 login_required() 裝飾器,則需要用下面的方法動態的去獲取 驗證成功要跳轉的頁面 # next_url = request.GET.get("next","/index/") # 認證成功后要跳轉的頁面;# 雖然此時為POST請求,但其 請求體中仍有 ?next="xxx"(表示驗證成功后跳轉的頁面),所以仍可用 request.GET的方式獲取該請求體 # return redirect(next_url) return render(request,"login.html") def index(request): print(request.user) print("request.user.username",request.user.username) # request.user.username 獲取 request.user 這個對象(記錄)的username對應的值 print("is_anonymous",request.user.is_anonymous) # request.user.is_anonymous:判斷是否為匿名用戶 # if request.user.is_anonymous: # 以匿名用戶的身份登陸 # 也可以用下面的方法去判斷: is_authenticated if not request.user.is_authenticated: return redirect("/login/") return render(request,"index.html") def logout(request): # 注銷接口:auth.logout(request) auth.logout(request) # 作用等同於: request.session.flush();# 會把 django_session 表中 相應的記錄刪除 return redirect("/login/") def reg(request): if request.method == "POST": username = request.POST.get("username") psw = request.POST.get("psw") # 注冊功能 # User.objects.create(username=username,password=psw) # 不要用 User 去create();因為這個create 出來的記錄是明文的 密碼 user = User.objects.create_user(username=username,password=psw) # 應該用 create_user() 或者 create_superuser(); 這兩種方法能將 password 變成 密文;返回值是插入的這條記錄對象 return redirect("/login/") return render(request,"reg.html") # 要實現的效果:is_authenticated 為True時才能。為了不在每次視圖函數中都自己寫 認證代碼的邏輯(避免重復),可利用 裝飾器: login_required() # 利用裝飾器 login_required() 時,需要在 settings.py 中設置 LOGIN_URL="/login/" (is_authenticated為False時所要跳轉的 路徑);而且在 login() 這個視圖函數中需要動態的去 獲取 登陸認證完之后所要跳轉的路徑(見login()) from django.contrib.auth.decorators import login_required @login_required # 登陸認證裝飾器 def order(request): # 由於有 login_required() 這個裝飾器,其內部不需要再寫認證的邏輯;該裝飾器實現的效果如下: # if not request.user.is_authenticated: # return redirect("/login/") return render(request,"order.html")
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>登陸</h3> <form action="" method="post"> {% csrf_token %} 用戶名 <input type="text" name="username"> 密碼 <input type="password" name="psw"> <input type="submit"> </form> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>hello {{ request.user }}</h3> <a href="/logout/">注銷</a> </body> </html>
reg.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>注冊</h3> <form action="" method="post"> {% csrf_token %} 用戶名 <input type="text" name="username"> 密碼 <input type="password" name="psw"> <input type="submit"> </form> </body> </html>
order.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>order</h3> </body> </html>
User對象其它的方法:
1. check_password(passwd) 用戶需要修改密碼的時候 首先要讓他輸入原來的密碼 ,如果給定的字符串通過了密碼檢查,返回 True 2. 修改密碼: user = User.objects.get(username='') user.set_password(password='') user.save
AnonymousUser相關屬性:
匿名用戶 class models.AnonymousUser django.contrib.auth.models.AnonymousUser 類實現了django.contrib.auth.models.User 接口,但具有下面幾個不同點: id 永遠為None。 username 永遠為空字符串。 get_username() 永遠返回空字符串。 is_staff 和 is_superuser 永遠為False。 is_active 永遠為 False。 groups 和 user_permissions 永遠為空。 is_anonymous() 返回True 而不是False。 is_authenticated() 返回False 而不是True。 set_password()、check_password()、save() 和delete() 引發 NotImplementedError。 New in Django 1.8: 新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User。
補充:如果自己寫的 用戶信息表 想和 Django自帶的 User表關聯(或者說 擴展 User 表的字段),可用以下方法:
from django.contrib.auth.models import User class UserInfo(models.Model): user = models.OneToOneField(User,on_delete=models.CASCADE) # User 不要加 "" name = models.CharField(max_length=32) # 添加擴展字段
# 登陸驗證時,要去 Django自帶的 User 表驗證
中間件
中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全局上改變django的輸入與輸出。因為改變的是全局,所以需要謹慎實用,用不好會影響到性能。
如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些都可以通過中間件來實現。
可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。
Django默認的Middleware
:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
自定義中間件:
中間件中一共有四個方法:
process_request
process_view
process_exception
process_response
process_request,process_response:(這兩個用的最多;通常情況下 process_reqeust 沒有返回值,但 process_response必須有返回值,返回值是視圖函數返回的響應體 response)
當用戶發起請求的時候會依次經過所有的的中間件,這個時候的請求是process_request,最后到達views的函數中,views函數處理后,在依次穿過中間件,這個時候是process_response,最后返回給請求者。
上述截圖中的中間件都是django中的,我們也可以自己定義一個中間件,我們可以自己寫一個類,但是必須繼承MiddlewareMixin
需要導入
from django.utils.deprecation import MiddlewareMixin
in views:
def index(request): print("view函數...") return HttpResponse("OK")
in Mymiddlewares.py:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Md1(MiddlewareMixin): def process_request(self,request): print("Md1請求") def process_response(self,request,response): print("Md1返回") return response class Md2(MiddlewareMixin): def process_request(self,request): print("Md2請求") #return HttpResponse("Md2中斷") def process_response(self,request,response): print("Md2返回") return response
結果:
# Md1請求 # Md2請求 # view函數... # Md2返回 # Md1返回
注意:如果當請求到達請求2的時候直接不符合條件返回,即return HttpResponse("Md2中斷"),程序將把請求直接發給中間件2返回,然后依次返回到請求者,結果如下:
返回Md2中斷的頁面,后台打印如下:
# Md1請求 # Md2請求 # Md2返回 # Md1返回
流程圖如下:
process_view:
process_view(self, request, callback, callback_args, callback_kwargs)
# callback表示對應的視圖函數(如:views.index),callback_args, callback_kwargs 表示視圖函數的參數
Mymiddlewares.py修改如下:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Md1(MiddlewareMixin): def process_request(self,request): print("Md1請求") #return HttpResponse("Md1中斷") def process_response(self,request,response): print("Md1返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Md1view") class Md2(MiddlewareMixin): def process_request(self,request): print("Md2請求") return HttpResponse("Md2中斷") def process_response(self,request,response): print("Md2返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Md2view")
結果如下:
# Md1請求 # Md2請求 # Md1view # Md2view # view函數... # Md2返回 # Md1返回
下圖進行分析上面的過程:
當最后一個中間的process_request到達路由關系映射之后,返回到中間件1的process_view,然后依次往下,到達views函數,最后通過process_response依次返回到達用戶。
process_view可以用來調用視圖函數:
class Md1(MiddlewareMixin): def process_request(self,request): print("Md1請求") #return HttpResponse("Md1中斷") def process_response(self,request,response): print("Md1返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): # return HttpResponse("hello") response=callback(request,*callback_args,**callback_kwargs) return response
結果如下:
# Md1請求 # Md2請求 # view函數... # Md2返回 # Md1返回
注意:process_view如果有返回值,會越過其他的process_view以及視圖函數,但是所有的process_response都還會執行。
process_exception:
process_exception(self, request, exception) # exception 表示錯誤信息
示例修改如下:
class Md1(MiddlewareMixin): def process_request(self,request): print("Md1請求") #return HttpResponse("Md1中斷") def process_response(self,request,response): print("Md1返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): # return HttpResponse("hello") # response=callback(request,*callback_args,**callback_kwargs) # return response print("md1 process_view...") def process_exception(self): print("md1 process_exception...") class Md2(MiddlewareMixin): def process_request(self,request): print("Md2請求") # return HttpResponse("Md2中斷") def process_response(self,request,response): print("Md2返回") return response def process_view(self, request, callback, callback_args, callback_kwargs): print("md2 process_view...") def process_exception(self): print("md1 process_exception...")
結果如下:
# Md1請求 # Md2請求 # md1 process_view... # md2 process_view... # view函數... # # Md2返回 # Md1返回
流程圖如下:
當views(視圖函數)出現錯誤時才會執行 process_exception:
將md2的process_exception修改如下:
def process_exception(self,request,exception): print("md2 process_exception...") return HttpResponse("error")
結果如下:
# Md1請求 # Md2請求 # md1 process_view... # md2 process_view... # view函數... # md2 process_exception... # Md2返回 # Md1返回
注:自定義的中間件要添加到 settings.py的 MIDDLEWARE 列表中