django自帶的用戶認證系統提供了訪問控制的的功能。
1.只允許登錄的用戶登錄
django的用戶可分為兩類,一是可認證的用戶,也就是在django.contrib.auth.models.User中注冊了的;另一種是匿名用戶
django.contrib.auth.models.AnonymousUser,每個訪問的未登錄的用戶都是該類的一個實例,而匿名用戶是無法認證的,即
is_authenticated 方法永遠返回 False,或者
is_anonymous返回True,我們可以在代碼邏輯中實現對匿名用戶進行判斷,然后拒絕其訪問(403),或者重定向到登錄頁面等。
from django.conf import settings from django.shortcuts import redirect def my_view(request): if not request.user.is_authenticated: return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path)) # ...
以上就是在代碼中重定向到登錄頁面的做法,也可以返回錯誤信息:
from django.shortcuts import render def my_view(request): if not request.user.is_authenticated: return render(request, 'myapp/login_error.html') # ...
由於這是一個常見的需求,所以django提供了一個裝飾器來實現這個功能。
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
這樣大大的簡化了工作。
login_required(redirect_field_name='next', login_url=None)
若用戶沒有登錄的話, 其將無法訪問被裝飾的視圖,而是重定向到登錄頁面,這個登錄頁面可以使用login_url參數指定,並且可以接受命名url的方式,如:@login_required(login_url='login'),當然前提是要有相應的命名的url。如果沒有給定這個參數,將使用
settings.LOGIN_URL的值。
Default: '/accounts/login/'
The URL where requests are redirected for login, especially when using the
login_required() decorator.
This setting also accepts
named URL patterns which can be used to reduce configuration duplication since you don’t have to define the URL in two places (settings and URLconf).
可以看見其有默認值,如果你不給參數,且登錄地址不是這個默認值,將觸發404錯誤,也就是還是重定向到那個頁面去了,只是找不到而已。若強制重寫為None,其會觸發TypeError,若DEBUG=False,則應該是500系列的服務器錯誤。
但是,在重定向的時候,並不是單純跳轉,而是會帶一個next查詢參數例如:
這樣你就可以獲取登錄前想要訪問的頁面(這里就是/account/change_info頁面了),然后在登錄后重定向回去,而不用盲目重定向到首頁,用戶體驗會更好點。
下面就是登錄視圖中所做的配合:
next_to = request.GET.get('next', None) # 獲取是否有next的重定向,是一個相對路徑,不含方案和域名 if next_to: return redirect(next_to)
當然你也可以改變redirect_field_name來改變next這個名稱,當然在登錄視圖里也要做想要的調整,也可以將其設為None來取消附帶參數的行為。
注意:login_required 並不會檢查用戶是否處於活躍狀態(is_active),而處理用戶登錄的默認后台模板在1.10之前並不會拒絕非活躍用戶登錄,而1.10版本就會。這意味着如果你使用的版本低於1.10,你必須在代碼邏輯中拒絕非活躍用戶登錄。
if user.is_active: # 若用戶是活躍的,即未凍結的,在1.10之前凍結用戶默認也能登錄,所以需要自己認證 login(request, user) # 登錄 ...... #其他處理 else: return HttpResponse('用戶被凍結')
2.只允許staff身份的用戶訪問某個視圖
django同樣提供了一個便捷的裝飾器來實現這個功能:
staff_member_required(redirect_field_name='next', login_url='admin:login')
可以看到其和上述的 login_required 參數上幾乎一樣,只是默認值有些許不同,而在用法上也是一樣的,但並沒有
settings.LOGIN_URL之類的設置層面上的退路。要注意一點,因為其默認是重定向至admin的登錄頁面,若要通過復雜化url的方式來隱藏入口的時候,要小心其會暴露該url。
This decorator is used on the admin views that require authorization. A view decorated with this function will having the following behavior:
這個裝飾器已經被admin的視圖所使用了,其行為如下:
- If the user is logged in, is a staff member (User.is_staff=True), and is active (User.is_active=True), execute the view normally.
如果用戶已經登錄,且為staff身份,並且是活躍的,則正常執行所裝飾的視圖。
- Otherwise, the request will be redirected to the URL specified by the login_url parameter, with the originally requested path in a query string variable specified by redirect_field_name. For example: /admin/login/?next=/admin/polls/question/3/.
否則,將會重定向到login_url,並帶查詢參數。
Example usage:
用例:
from django.contrib.admin.views.decorators import staff_member_required @staff_member_required def my_view(request): ...
可以看到其使用方法和
login_required()基本是一樣的。
3.簡單的函數測試驗證法
django提供了一個裝飾器,讓我們可以自定義一個簡單的驗證函數,只有通過該驗證函數之后才能訪問指定的視圖。
user_passes_test(func[, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME])
可以看到其比上面的兩個裝飾器多了一個參數,這個參數就是用指定驗證函數的。
例如,我想要驗證某個用戶的郵箱是否符合我想要的格式:
原始的驗證法:
from django.shortcuts import redirect def my_view(request): if not request.user.email.endswith('@example.com'): return redirect('/login/?next=%s' % request.path) # ...
在視圖函數中驗證其是否以@example.com結尾。
下面是裝飾器驗證法:
from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith('@example.com') @user_passes_test(email_check) def my_view(request): ...
注意:email_check函數其本質上也是返回布爾值,所以在自定義函數的時候,返回True表示通過,False表示不通過。不通過則重定向到登錄頁面,其他細節和之前的兩個裝飾器一樣。
基於類的視圖的實現
基於類的視圖在實現login_required的行為時,需要繼承LoginRequiredMixin這個類,並將其放在繼承樹的最左邊,同時相應的實現在類中書寫,例如:
from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/login/' redirect_field_name = 'redirect_to'
注:這里是View是通用視圖,這里並沒要導入的代碼,詳情請參考基於類的視圖。
還有更多的屬性可供使用,可參考
AccessMixin 這個類。
實現user_passes_test行為也是類似的,首先繼承UserPassesTestMixin這個類(在最左邊),然后在類的里面重寫test_func方法,該方法也是返回布爾值。
New in Django 1.9.
from django.contrib.auth.mixins import UserPassesTestMixin class MyView(UserPassesTestMixin, View): def test_func(self): return self.request.user.email.endswith('@example.com')
注:上述的兩個也是不驗證是否為活躍用戶的,所以使用的時候要小心。
