1. 程式人生 > >Django元件之使用者認證和中介軟體

Django元件之使用者認證和中介軟體

使用者認證元件

功能:用session記錄登入驗證狀態

前提:使用者表 django自帶的auth_user

建立超級使用者python manage.py createsuperuser

API 如下

一、 auth模組

  1: from django.contrib import auth

django.contrib.auth中提供了許多方法,這裡主要介紹其中的三個

1.1 authenticate()

提供了使用者認證,即驗證使用者名稱以及密碼是否正確,一般需要username password兩個關鍵字引數</p>如果認證資訊有效,會返回一個 User 物件。

authenticate()會在User 物件上設定一個屬性標識那種認證後端認證了該使用者,且該資訊在後面的登入過程中是需要的。

當我們試圖登陸一個從資料庫中直接取出來不經過authenticate()的User物件會報錯的!!

  1: user = authenticate(username='someone',password='somepassword')

1.2 login(HttpRequest, user)

該函式接受一個HttpRequest物件,以及一個認證了的User物件
此函式使用django的session框架給某個已認證的使用者附加上session id等資訊。

  1: def my_view(request):
  2:     username = request.POST['username']
  3:     password = request.POST['password']
  4:     user = authenticate(username=username, password=password)
  5:     if user is not None:
  6:         login(request, user)
  7:     # Redirect to a success page.
  8:     else
:
  9:         pass
 10:     # Return an 'invalid login' error message.
 11: 

1.3 logout(request) 登出使用者

  1: from django.contrib.auth import logout
  2: def logout_view(request):
  3:     logout(request)
  4:     # Redirect to a success page.

該函式接受一個HttpRequest物件,無返回值。當呼叫該函式時,當前請求的session資訊會全部清除。該使用者即使沒有登入,使用該函式也不會報錯。

二、User物件

User 物件屬性:username, password(必填項)password用雜湊演算法儲存到資料庫.

2.1 user物件的 is_authenticated()

如果是真正的 User 物件,返回值恆為 True 。 用於檢查使用者是否已經通過了認證。

通過認證並不意味著使用者擁有任何許可權,甚至也不檢查該使用者是否處於啟用狀態,這只是表明使用者成功的通過了認證。

這個方法很重要, 在後臺用request.user.is_authenticated()判斷使用者是否已經登入,如果true則可以向前臺展示request.user.name

現在有如下要求:
1 使用者登陸後才能訪問某些頁面,
2 如果使用者沒有登入就訪問該頁面的話直接跳到登入頁面
3 使用者在跳轉的登陸介面中完成登陸後,自動訪問跳轉到之前訪問的地址

方法1:

  1: def my_view(request):
  2:   if not request.user.is_authenticated():
  3:     return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
  4: 

方法2:

django已經為我們設計好了一個用於此種情況的裝飾器:@login_requierd

  1: from django.contrib.auth.decorators import login_required
  2: @login_required
  3: def my_view(request):
  4:     pass
  5: 

若使用者沒有登入,則會跳轉到django預設的 登入URL '/accounts/login/ ' (這個值可以在settings檔案中通過LOGIN_URL進行修改)。並傳遞 當前訪問url的絕對路徑 (登陸成功後,會重定向到該路徑)。

2.2 建立使用者

  1: from django.contrib.auth.models import User
  2: user = User.objects.create_user(username='',password='',email='')

2.3 check_password(password)

使用者需要修改密碼的時候 首先要讓他輸入原來的密碼 ,如果給定的字串通過了密碼檢查,返回 True

2.4 修改密碼

  1: user = User.objects.get(username='')
  2: user.set_password(password='')
  3: user.save()
  4: 

2.5 簡單示例

註冊

  1: def sign_up(request):
  2:     state = None
  3:     if request.method == 'POST':
  4:         password = request.POST.get('password', '')
  5:         repeat_password = request.POST.get('repeat_password', '')
  6:         email=request.POST.get('email', '')
  7:         username = request.POST.get('username', '')
  8:         if User.objects.filter(username=username):
  9:                 state = 'user_exist'
 10:         else:
 11:                 new_user = User.objects.create_user(username=username, password=password,email=email)
 12:                 new_user.save()
 13:                 return redirect('/book/')
 14:     content = {
 15:         'state': state,
 16:         'user': None,
 17:     }
 18:     return render(request, 'sign_up.html', content)
 19: 

修改密碼:

  1: @login_required
  2: def set_password(request):
  3:     user = request.user
  4:     state = None
  5:     if request.method == 'POST':
  6:         old_password = request.POST.get('old_password', '')
  7:         new_password = request.POST.get('new_password', '')
  8:         repeat_password = request.POST.get('repeat_password', '')
  9:         if user.check_password(old_password):
 10:             if not new_password:
 11:                 state = 'empty'
 12:             elif new_password != repeat_password:
 13:                 state = 'repeat_error'
 14:             else:
 15:                 user.set_password(new_password)
 16:                 user.save()
 17:                 return redirect("/log_in/")
 18:         else:
 19:             state = 'password_error'
 20:     content = {
 21:         'user': user,
 22:         'state': state,
 23:     }
 24: return render(request, 'set_password.html', content)
 25: 

補充

匿名使用者的特徵:

Id永遠為none,username永遠為空字串,get_username()永遠返回空字串,is_staff和is_superuser永遠為false,is_active永遠為False……

總的來說不是空就是flase。

中介軟體

Middleware

中介軟體顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全域性上改變django的輸入與輸出。因為改變的是全域性,所以需要謹慎實用,用不好會影響到效能。

如果你想修改請求,例如被傳送到view中的HttpRequest物件。 或者你想修改view返回的HttpResponse物件,這些都可以通過中介軟體來實現。

可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。

Django有預設的Middleware,每一箇中間件都有具體的功能。

自定義中介軟體

中介軟體中一共有四個方法:

process_request
process_view
process_exception
process_response

當用戶發起請求的時候會依次經過所有的的中介軟體,這個時候的請求時process_request,最後到達views的函式中,views函式處理後,在依次穿過中介軟體,這個時候是process_response,最後返回給請求者。

image

上述截圖中的中介軟體都是django中的,我們也可以自己定義一箇中間件,我們可以自己寫一個類,但是必須繼承MiddlewareMixin

匯入

  1: from django.utils.deprecation import MiddlewareMixin

 image

  1: from django.utils.deprecation import MiddlewareMixin
  2: from django.shortcuts import HttpResponse
  3: 
  4: class Md1(MiddlewareMixin):
  5:     def process_request(self,request):
  6:         print("Md1請求")
  7:     def process_response(self,request,response):
  8:         print("Md1返回")
  9:         return response
 10: class Md2(MiddlewareMixin):
 11:     def process_request(self,request):
 12:         print("Md2請求")
 13:         #return HttpResponse("Md2中斷")
 14:     def process_response(self,request,response):
 15:         print("Md2返回")
 16:         return response

意:如果當請求到達請求2的時候直接不符合條件返回,即return HttpResponse("Md2中斷"),程式將把請求直接發給中介軟體2返回,然後依次返回到請求者

流程圖如下:

image

process_view

  1: process_view(self, request, callback, callback_args, callback_kwargs)

流程圖如下:

image

當最後一箇中間的process_request到達路由關係對映之後,返回到中介軟體1的process_view,然後依次往下,到達views函式,最後通過process_response依次返回到達使用者。

process_view可以用來呼叫檢視函式:

  1: class Md1(MiddlewareMixin):
  2:     def process_request(self,request):
  3:         print("Md1請求")
  4:         #return HttpResponse("Md1中斷")
  5:     def process_response(self,request,response):
  6:         print("Md1返回")
  7:         return response
  8:     def process_view(self, request, callback, callback_args, callback_kwargs):
  9:         # return HttpResponse("hello")
 10:         response=callback(request,*callback_args,**callback_kwargs)
 11:         return response
 12: 

注意:process_view如果有返回值,會越過其他中介軟體的process_view以及檢視函式,但是所有的process_response都還會執行

process_exception

  1: process_exception(self, request, exception)

流程圖如下:

當views出現錯誤時:

image

示例:

  1: class Md1(MiddlewareMixin):
  2: def process_request(self,request):
  3:         print("Md1請求")
  4:         #return HttpResponse("Md1中斷")
  5:     def process_response(self,request,response):
  6:         print("Md1返回")
  7:         return response
  8:     def process_view(self, request, callback, callback_args, callback_kwargs):
  9:         # return HttpResponse("hello")
 10:         # response=callback(request,*callback_args,**callback_kwargs)
 11:         # return response
 12:         print("md1 process_view...")
 13:     def process_exception(self):
 14:         print("md1 process_exception...")
 15: class Md2(MiddlewareMixin):
 16:     def process_request(self,request):
 17:         print("Md2請求")
 18:         # return HttpResponse("Md2中斷")
 19:     def process_response(self,request,response):
 20:         print("Md2返回")
 21:         return response
 22:     def process_view(self, request, callback, callback_args, callback_kwargs):
 23:         print("md2 process_view...")
 24:     def process_exception(self):
 25:         print("md1 process_exception...")
 26: 

列印結果如下

  1: Md1請求
  2: Md2請求
  3: md1 process_view...
  4: md2 process_view...
  5: view函式...
  6: 
  7: Md2返回
  8: Md1返回

將md2的process_exception修改如下:

  1: def process_exception(self, request, exception):
  2:     print("md2 process_exception...")
  3:     return HttpResponse("error")
  4: 

結果如下:

  1: Md1請求
  2: Md2請求
  3: md1 process_view...
  4: md2 process_view...
  5: view函式...
  6: md2 process_exception...
  7: Md2返回
  8: Md1返回
  9: 

中介軟體應用案例

1、做IP訪問頻率限制

某些IP訪問伺服器的頻率過高,進行攔截,比如限制每分鐘不能超過20次。

2、URL訪問過濾

如果使用者訪問的是login檢視(放過)
如果訪問其他檢視,需要檢測是不是有session認證,已經有了放行,沒有返回login,這樣就省得在多個檢視函式上寫裝飾器了!

程式碼示例

  1: from django.utils.deprecation import MiddlewareMixin
  2: from django.shortcuts import redirect
  3: from authDemo import settings
  4: 
  5: class AuthMiddleware(MiddlewareMixin) :
  6: 
  7:     def process_request(self, request) :
  8:         # 新增一個url白名單
  9:         white_list = settings.WHITE_LIST
 10:         if request.path in white_list :
 11:             return None
 12: 
 13:         if not request.user.is_authenticated :
 14:             return redirect("/login/")
 15: