首先看middleware的定義:
python
auth模塊有兩個middleware:AuthenticationMiddleware和SessionAuthenticationMiddleware。數據庫
AuthenticationMiddleware負責向request添加user屬性django
class AuthenticationMiddleware(object): def process_request(self, request): assert hasattr(request, 'session'), ( "The Django authentication middleware requires session middleware " "to be installed. Edit your MIDDLEWARE_CLASSES setting to insert " "'django.contrib.sessions.middleware.SessionMiddleware' before " "'django.contrib.auth.middleware.AuthenticationMiddleware'." ) request.user = SimpleLazyObject(lambda: get_user(request))
能夠看見AuthenticationMiddleware首先檢查是否由session屬性,由於它須要session存儲用戶信息。緩存
user屬性的添加,被延遲到了get_user()函數裏。SimpleLazyObject是一種延遲的技術。服務器
在來看SessionAuthenticationMiddleware的定義:session
它負責session驗證函數
class SessionAuthenticationMiddleware(object): """ Middleware for invalidating a user's sessions that don't correspond to the user's current session authentication hash (generated based on the user's password for AbstractUser). """ def process_request(self, request): user = request.user if user and hasattr(user, 'get_session_auth_hash'): session_hash = request.session.get(auth.HASH_SESSION_KEY) session_hash_verified = session_hash and constant_time_compare( session_hash, user.get_session_auth_hash() ) if not session_hash_verified: auth.logout(request)
經過比較user的get_session_auth_hash方法,和session裏面的auth.HASH_SESSION_KEY屬性,判斷用戶的session是否正確。ui
至於request裏面的user對象,由有什麼屬性,須要看看get_user()函數的定義。this
def get_user(request): if not hasattr(request, '_cached_user'): request._cached_user = auth.get_user(request) return request._cached_user
顯然get_user方法在request增長了_cached_user屬性,用來做爲緩存。spa
由於用戶認證須要查詢數據庫,獲得用戶的信息,因此減小開銷是有必要的。
注意,這種緩存只針對同一個request而言的,即在一個view中屢次訪問request.user屬性。
每次http請求都是新的request。
再接着看auth.get_user()方法的定義,深刻了解request.user這個對象:
def get_user(request): """ Returns the user model instance associated with the given request session. If no user is retrieved an instance of `AnonymousUser` is returned. """ from .models import AnonymousUser user = None try: user_id = request.session[SESSION_KEY] backend_path = request.session[BACKEND_SESSION_KEY] except KeyError: pass else: if backend_path in settings.AUTHENTICATION_BACKENDS: backend = load_backend(backend_path) user = backend.get_user(user_id) return user or AnonymousUser()
首先它會假設客戶端和服務器已經創建session機制了,這個session中的SESSION_KEY屬性,就是user的id號。
這個session的BACKEND_SESSION_KEY屬性,就是指定使用哪一種後臺技術獲取用戶信息。最後使用backend.get_user()獲取到user。若是不知足,就返回AnonymousUser對象。
從這個獲取user的過程,首先有個前提,就是客戶端與服務端得先創建session機制。那麼這個session機制是怎麼創建的呢?
這個session創建的過程在auth.login函數裏:
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 request.session[SESSION_KEY] != 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.pk 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)
首先它會判斷是否存在與用戶認證相關的session,若是有就清空數據,若是沒有就新建。
而後再寫如session的值:SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY。
而後講一下登陸時,使用auth一般的作法:
from django.contrib.auth import authenticate, login def login_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: login(request, user) # 轉到成功頁面 else: # 返回錯誤信息
通常提交經過POST方式提交,而後調用authenticate方法驗證,成功後使用login建立session。
繼續看看authenticate的定義:
def authenticate(**credentials): """ If the given credentials are valid, return a User object. """ for backend in get_backends(): 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 = "%s.%s" % (backend.__module__, backend.__class__.__name__) return user # The credentials supplied are invalid to all backends, fire signal user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials))
它會去輪詢backends,經過調用backend的authenticate方法認證。
注意它在後面更新了user的backend屬性,代表此用戶是使用哪一種backend認證方式。它的值會在login函數裏,被存放在session的BACKEND_SESSION_KEY屬性裏。
經過backend的authenticate方法返回的user,是沒有這個屬性的。
最後說下登陸之後auth的用法。上面展現了登陸時auth的用法,在登陸之後,就會創建session機制。因此直接獲取request的user屬性,就能夠判斷用戶的信息和狀態。
def my_view(request): if request.user.is_authenticated(): # 認證的用戶 else: # 匿名用戶