django.contirb.auth-認證

首先看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:    
        # 匿名用戶
相關文章
相關標籤/搜索