上面兩篇文章,講述的Django的Authentication系統的核心模型對象User API和相關的使用,本文繼續深入,討論如何在Web中使用Authentication系統。
前面說了,Django的這套權限系統有三個核心,User,Permission,Group。
而在Web應用中,任何的權限系統要做的第一步就是用戶識別,也就是我們常說的登陸(login)。只有正確的登陸校驗,知道用戶是誰了,才能夠知道用戶能干什么,那就是許可(Permission)需要負責解決的事情,而Group則是批量設置許可的時候的一個便利手段了。
Web請求的認證:
django有一套方法,可以在每個view方法能夠接收到的request對象中增加權限驗證相關的方法。要做到這一點,首先需要:
- 安裝SessionMiddleware和AuthenticationMiddleware。安裝方法在settings文件中對MIDDLEWARE_CLASSES變量增加上述兩個Middleware類。Middleware的設計幾乎在任何web框架中都有體現,只不過叫法和具體實現的手段不盡相同,例如在struts2中叫做interceptor攔截器,采用函數遞歸Stack的方式實現(其它框架沒見過有這么干的)。設計思路都是在request進入最后的處理方法之前經過一個個前處理,在處理方法處理完之后再經過一個個后處理,前后的次序恰好相反。有點點走題,請看例子:
1 MIDDLEWARE_CLASSES = ( 2 'django.contrib.sessions.middleware.SessionMiddleware', 3 'django.middleware.locale.LocaleMiddleware', 4 'django.middleware.common.CommonMiddleware', 5 'django.middleware.csrf.CsrfViewMiddleware', 6 'django.contrib.auth.middleware.AuthenticationMiddleware', 7 'django.contrib.messages.middleware.MessageMiddleware', 8 'django.middleware.transaction.TransactionMiddleware', 9 )
一旦安裝好了之后,在view中,我們就可以使用request.user獲取當前的登陸用戶User對象。如果當前用戶沒有登陸,那么request.user將會是我們之前所說的AnonymousUser對象。我們可以用User對象的is_authenticated()方法將這兩者區分開來:
if
request.user.is_authenticated():
# 做一些事情針對驗證用戶.
else
:
# 做一些事情對於匿名未登錄用戶.
那么如何登陸一個用戶呢?
需要兩個函數:authenticate(username,password)和login(request,user),位於django.contrib.auth模塊中;
這兩個方法需要結合使用,
1.authenticate(username,password)函數需要兩個參數username,password,如果校驗通過則返回User對象,如果校驗不通過返回None,例如:
from
django.contrib.auth
import
authenticate
user
=
authenticate(username
=
'john'
, password
=
'secret'
)
if
user
is
not
None
:
if
user.is_active:
print
"You provided a correct username and password!"
else
:
print
"Your account has been disabled!"
else
:
print
"Your username and password were incorrect."
2.login接受兩個參數,第一個是request對象,第二個是user對象。login方法使用SessionMiddleware將userID存入session當中。注意,在用戶還未登錄的時候,也存在着匿名用戶的Session,在其登陸之后,之前在匿名Session中保留的信息,都會保留下來。這兩個方法要結合使用,而且必須要先調用authenticate(),因為該方法會User的一個屬性上紀錄該用戶已經通過校驗了,這個屬性會被隨后的login過程所使用,例如:
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
:
if
user.is_active:
login(request, user)
# 跳轉到成功頁面.
else
:
# 返回一個無效帳戶的錯誤
else
:
# 返回登錄失敗頁面。
我們也可以不用authenticate()進行特定於一個用戶的身份校驗,直接使用和User無關的幾個函數進行密碼相關的校驗,在Django1.4中以及新版本中提供以下方法,位於模塊django.contrib.auth.hashers:
- check_password(password,encoded):第一個參數是明文密碼,第二個參數是加密過的密碼。如果通過校驗返回True,不通過返回False;
- make_password(password[,salt,hashers]):根據給定的明文密碼,salt,和Django支持的加密算法,返回一個加密的密碼。如果password提供的值為None,那么該返回值將永遠通不過check_password()方法。這個返回值是一個特定的約定值,目前是'!';
- is_password_usable(encoded_password):判斷是否給定字符串是一個hashed密碼,有機會通過check_password()函數的校驗。
接下來,如何登出(log out)一個用戶?
我們使用django.contrib.auth.logout函數來登出用django.contrib.auth.login函數登入的用戶。
logout(requet)
函數只有一個參數,就是request。沒有返回值,而且即使當前用戶沒有登陸也不會拋出任何異常。
例子:
from
django.contrib.auth
import
logout
def
logout_view(request):
logout(request)
# 重定向到成功登出界面
這個方法,會將存儲在用戶session的數據全部清空,這樣避免有人用當前用戶的瀏覽器登陸然后就可以查看當前用戶的數據了,回想一下login會保留anonymous用戶的session數據。如果需要將一些東西加入到登出之后的用戶session,那么需要在logout方法調用之后再進行。
接下來介紹Login和Logout的兩個signals
Django的signal體系是一套簡單實用的事件定義、事件產生、事件監聽、事件處理框架,具體可以參看Django關於signal的文檔。在登陸和登出這兩個重要的點上,提供了兩個signal:
- django.contrib.auth.signals.user_logged_in
- django.contrib.auth.signals.user_logged_out
有三個參數會隨singal傳過來:
- sender:user的class,如果是logout事件該值有可能是None如果用戶根本就沒有驗證通過。
- request:HttpRequest對象
- user:user對象,如果是logout事件該值有可能是None如果用戶根本就沒有驗證通過。
一個經常性的簡單需求就是控制某些view(struts中叫做action方法)只對登陸用戶開放,如果未登錄用戶請求該view則跳轉到登錄界面讓其登陸。要做到這一點,我們可以這樣做:
from
django.http
import
HttpResponseRedirect
def
my_view(request):
if
not
request.user.is_authenticated():
return
HttpResponseRedirect(
'/login/?next=%s'
%
request.path)
# ...
也可以這樣做,返回一個錯誤的頁面:
def
my_view(request):
if
not
request.user.is_authenticated():
return
render_to_response(
'myapp/login_error.html'
)
# ...
更為優雅的方式是用decorator:
django.contrib.auth.decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME,login_url=None])
login_required()裝飾器函數做了以下事情:
- 如果當前用戶沒有登陸,跳轉到settings.LOGIN_URL,並傳遞當前的絕對路徑到URL請求參數中,例如:/accounts/login/?next=/polls/3/
- 如果當前用戶已經登陸了,執行view方法。在view中的方法可以認為當前用戶已經登陸了。
login_required方法接受兩個參數:
- redirect_field_name:默認值是next。用來定義登陸成功之后的跳回之前訪問界面的url。
- login_url:默認值是settings.LOGIN_URL。用來指定登陸界面的url。如果不傳入改參數,就需要確保settings.LOGIN_URL的值是正確設置的。
沒有參數的login_required裝飾器使用方法:
from
django.contrib.auth.decorators
import
login_required
@login_required
def
my_view(request):
...
傳遞參數的方法:
from
django.contrib.auth.decorators
import
login_required
@login_required
(redirect_field_name
=
'my_redirect_field'
)
def
my_view(request):
...
from
django.contrib.auth.decorators
import
login_required
@login_required
(login_url
=
'/accounts/login/'
)
def
my_view(request):
...
以上就是Django提供的用於完成login和logout相關的一些API支持,使用他們可以很好的對用戶進行認證了,也就是說用戶是誰系統已經搞清楚了,接下來就是更細粒度的判斷,判斷此人究竟能做些什么,也就是Permission許可的使用了。請看 Django中的權限控制-authentication-內置版本-4。

