Django原始碼分析之許可權系統_擒賊先擒王
乍見
Django內建的許可權系統已經很完善了,加上django-guardian提供的功能,基本上能滿足大部分的許可權需求。暫且不說django-guardian,我們先來看下Django內建的許可權系統:django.contrib.auth 包。
相識
一般許可權系統分為全域性許可權和物件許可權。Django只提供了一個物件許可權的框架,具體實現由第三方庫django-gardian完成。我們只看全域性許可權。
先來看auth包暴露出哪些介面。
django.contrib.auth.__init__.py
def load_backend(path):
return import_string(path)()
def _get_backends(return_tuples=False):
backends = []
for backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
backends.append((backend, backend_path) if return_tuples else backend)
if not backends:
raise ImproperlyConfigured(
`No authentication backends have been defined. Does `
`AUTHENTICATION_BACKENDS contain anything?`
)
return backends
def get_backends():
return _get_backends(return_tuples=False)
前三個方法都是為了載入backends。一個backend其實就是一個class,必須實現authenticate和get_user兩個方法。每當我們這樣驗證使用者時
authenticate(username=`username`, password=`password`)
django就會去呼叫這些backend class,用其提供的方法去驗證使用者許可權。那django是如何知道要呼叫哪些backend class呢?答案就在settings.py中,預設為
AUTHENTICATION_BACKENDS = [`django.contrib.auth.backends.ModelBackend`]
那Django是如何呼叫這些backend class的呢?
def authenticate(**credentials):
"""
If the given credentials are valid, return a User object.
"""
for backend, backend_path in _get_backends(return_tuples=True):
try:
inspect.getcallargs(backend.authenticate, **credentials)
except TypeError:
# This backend doesn`t accept these credentials as arguments. Try the next one.
continue
try:
user = backend.authenticate(**credentials)
except PermissionDenied:
# This backend says to stop in our tracks - this user should not be allowed in at all.
return None
if user is None:
continue
# Annotate the user object with the path of the backend.
user.backend = backend_path
return user
# The credentials supplied are invalid to all backends, fire signal
user_login_failed.send(sender=__name__,
credentials=_clean_credentials(credentials))
由此可見,Django會在第一個驗證正確的backend class呼叫完成後停止,或者碰到PermissionDenied異常也會停止,所以backend class的順序也很重要。可以新增自定義的backend class。
def login(request, user):
"""
Persist a user id and a backend in the request. This way a user doesn`t
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.
"""
session_auth_hash = ``
if user is None:
user = request.user
if hasattr(user, `get_session_auth_hash`):
session_auth_hash = user.get_session_auth_hash()
if SESSION_KEY in request.session:
if _get_user_session_key(request) != user.pk or (
session_auth_hash and
request.session.get(HASH_SESSION_KEY) != session_auth_hash):
# To avoid reusing another user`s session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
request.session.flush()
else:
request.session.cycle_key()
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = user.backend
request.session[HASH_SESSION_KEY] = session_auth_hash
if hasattr(request, `user`):
request.user = user
rotate_token(request)
user_logged_in.send(sender=user.__class__, request=request, user=user)
login方法,顧名思義,登入使用者,同時設定好session,最後傳送登入成功通知
def logout(request):
"""
Removes the authenticated user`s ID from the request and flushes their
session data.
"""
# Dispatch the signal before the user is logged out so the receivers have a
# chance to find out *who* logged out.
user = getattr(request, `user`, None)
if hasattr(user, `is_authenticated`) and not user.is_authenticated():
user = None
user_logged_out.send(sender=user.__class__, request=request, user=user)
# remember language choice saved to session
language = request.session.get(LANGUAGE_SESSION_KEY)
request.session.flush()
if language is not None:
request.session[LANGUAGE_SESSION_KEY] = language
if hasattr(request, `user`):
from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
相對的,logout方法,負責登出使用者,清理session,最後設定當前使用者為匿名使用者
def get_user_model():
"""
Returns the User model that is active in this project.
"""
try:
return django_apps.get_model(settings.AUTH_USER_MODEL)
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form `app_label.model_name`")
except LookupError:
raise ImproperlyConfigured(
"AUTH_USER_MODEL refers to model `%s` that has not been installed" % settings.AUTH_USER_MODEL
)
Django不推薦直接使用User class,而是通知get_user_model方法獲取當前的使用者class(或者使用settins.AUTH_USER_MODEL)。這是為了防止因為開發者使用了自定義使用者class而導致的資訊錯誤。
def update_session_auth_hash(request, user):
"""
Updating a user`s password logs out all sessions for the user if
django.contrib.auth.middleware.SessionAuthenticationMiddleware is enabled.
This function takes the current request and the updated user object from
which the new session hash will be derived and updates the session hash
appropriately to prevent a password change from logging out the session
from which the password was changed.
"""
if hasattr(user, `get_session_auth_hash`) and request.user == user:
request.session[HASH_SESSION_KEY] = user.get_session_auth_hash()
最後這個方法的使用場景很少。一般我們更新使用者密碼時,會在session中清除使用者登入資訊,導致使用者需要重新登入。而使用update_session_auth_hash我們就可以在更新使用者密碼的同時更新使用者的session資訊,這樣,使用者就不需要重新登入了。
回想
擒賊先擒王,以上都是django.contrib.auth包中的__init__.py入口檔案中的內容,背後還有很多“能工巧匠”,否則怎麼支撐起auth整套許可權系統?後續文章會一一介紹。
相關文章
- 擒賊先擒王,簡單談一下JavaScript作用域鏈(Scope Chain)JavaScriptAI
- Django(63)drf許可權原始碼分析與自定義許可權Django原始碼
- 擴充套件.Django-許可權系統套件Django
- Django-Rest-Framework 許可權管理原始碼淺析DjangoRESTFramework原始碼
- Oracle的物件許可權、角色許可權、系統許可權Oracle物件
- django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解Django框架
- Android系統許可權和root許可權Android
- MySQL許可權系統MySql
- Oracle系統許可權Oracle
- Linux作業系統的許可權程式碼分析【轉】Linux作業系統
- drf 許可權校驗設定與原始碼分析原始碼
- Oracle 使用者、物件許可權、系統許可權Oracle物件
- 許可權之選單許可權
- mongodb 的許可權系統MongoDB
- 【JavaWeb】許可權管理系統JavaWeb
- 有贊許可權系統
- Android系統許可權Android
- 許可權系統設計
- 許可權系統跟進
- 動態許可權的使用以及RxPermissions原始碼分析原始碼
- 一對一直播系統原始碼,後臺管理系統許可權控制方案原始碼
- 短視訊系統原始碼,讓程式獲得通知欄許可權原始碼
- 許可權維持專題:作業系統許可權維持作業系統
- 企業許可權管理系統
- Winner許可權管理系統3.0
- MySQL許可權系統簡介MySql
- 通用許可權系統介紹
- 許可權系統概要(收集,整理)
- Mysql存取許可權系統(轉)MySql
- Django實戰1-許可權管理功能實現-04:系統入口Django
- 一對一原始碼,前端頁面許可權和按鈕許可權控制原始碼前端
- Spring security(五)-完美許可權管理系統(授權過程分析)Spring
- 適配懸浮窗許可權與系統設定修改許可權
- 系統許可權傳遞和物件許可權傳遞的測試物件
- 檢視角色裡包含的系統許可權、物件許可權和角色物件
- [Abp vNext 原始碼分析] - 7. 許可權與驗證原始碼
- 管理系統之許可權的設計和實現
- Django restframework 框架筆記 (二) 許可權DjangoRESTFramework框架筆記