django Admin 로그인 인증 프로세스 소스 분석
urlpatterns = [
url(r'^admin/', admin.site.urls),
바로 관리자로 가세요.site.urls가 가서 그 원본 코드는 다음과 같다.
@property
def urls(self):
return self.get_urls(), 'admin', self.name
여기 바로 self를 보세요.get_urls () 함수면 됩니다. 다음 두 개는 url 함수의 매개 변수입니다.get_urls 함수의 소스 코드는 다음과 같습니다.
def get_urls(self):
from django.conf.urls import url, include
#
urlpatterns = [
url(r'^$', wrap(self.index), name='index'),
url(r'^login/$', self.login, name='login'),
여기에서 우리는 마지막 url(r'^login/$', self.login,name='login'), 즉self를 직접 주목한다.login 함수, 그의 원본 코드는 다음과 같다.
@never_cache
def login(self, request, extra_context=None):
"""
Displays the login form for the given HttpRequest.
"""
if request.method == 'GET' and self.has_permission(request):
# Already logged-in, redirect to admin index
index_path = reverse('admin:index', current_app=self.name)
return HttpResponseRedirect(index_path)
from django.contrib.auth.views import login
# Since this module gets imported in the application's root package,
# it cannot import models from other applications at the module level,
# and django.contrib.admin.forms eventually imports User.
from django.contrib.admin.forms import AdminAuthenticationForm
context = dict(
self.each_context(request),
title=_('Log in'),
app_path=request.get_full_path(),
username=request.user.get_username(),
)
if (REDIRECT_FIELD_NAME not in request.GET and
REDIRECT_FIELD_NAME not in request.POST):
context[REDIRECT_FIELD_NAME] = reverse('admin:index', current_app=self.name)
context.update(extra_context or {})
defaults = {
'extra_context': context,
'authentication_form': self.login_form or AdminAuthenticationForm,
'template_name': self.login_template or 'admin/login.html',
}
request.current_app = self.name
return login(request, **defaults)
이 함수 앞에는 상하문 환경의 검측과 준비를 하고 마지막으로django/contrib/auth/views에 들어갑니다.py의 login 함수, 이 함수의 원본 코드는 다음과 같습니다.
@deprecate_current_app
@sensitive_post_parameters()
@csrf_protect
@never_cache
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
extra_context=None, redirect_authenticated_user=False):
"""
Displays the login form and handles the login action.
"""
redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
if redirect_authenticated_user and request.user.is_authenticated:
redirect_to = _get_login_redirect_url(request, redirect_to)
if redirect_to == request.path:
raise ValueError(
"Redirection loop for authenticated user detected. Check that "
"your LOGIN_REDIRECT_URL doesn't point to a login page."
)
return HttpResponseRedirect(redirect_to)
elif request.method == "POST":
form = authentication_form(request, data=request.POST)
if form.is_valid(): #
auth_login(request, form.get_user()) #
return HttpResponseRedirect(_get_login_redirect_url(request, redirect_to))
else:
form = authentication_form(request)
current_site = get_current_site(request)
context = {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context)
이 함수 안에서 우리는 form에 중점을 두었다.is_valid () 함수, form=authenticationform,authentication_form은 login 함수의 매개 변수인 AuthenticationForm입니다. 그는forms를 계승합니다.Form,forms.Form은 BaseForm을 상속합니다.그래서 바로 form을 봐요.is_valid 함수는 뭘 했으면 좋겠어요.django/form/forms.py중,isvalid 함수의 소스는 다음과 같습니다.
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not self.errors
마지막return에서self가 호출된 것을 보았습니다.errors, 그럼 계속해서 errors 함수가 구체적으로 무엇을 했는지 볼까요?errors 소스는 다음과 같습니다.
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
여기 먼저 self.errors, AuthenticationForm의init_함수 중self.errors = None # Stores the errors after clean () has been called, 그리고 전체 소스에서도self를 호출하는 것을 볼 수 있습니다.full_clean () 이전 self.errors = None이 항상 진실이라면 self를 사용하십시오.full_clean 함수.계속해서 self를 보세요.full_clean의 소스:
def full_clean(self):
"""
Cleans all of self.data and populates self._errors and
self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form() #
self._post_clean()
여기 포인트clean_form 함수, 모든 웹 페이지의 로그인은 form 폼을 제출한 다음에 사용자 이름과 비밀번호를 검증하는 것이다.django도 예외가 아니다.self._clean_form ()에서 self가 호출되었습니다.clean 함수, 즉 AuthenticationForm의 clean 함수입니다. AuthenticationForm의 clean 함수 원본을 직접 보십시오.
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
self.user_cache = authenticate(username=username, password=password)#
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
clean 함수에서 마침내 우리가 가장 보고 싶은 논리, 즉 POST에서 사용자 이름과 비밀번호를 얻고 authenticate를 실행하여 로그인 인증을 하는 것을 보았다.(여기에 작은 의문이 하나 있다. self.cleaned data는self.field clean 함수에서 초기화되었지만,self.fields=copy.deepcopy(self.base fields)에서의self.base_fields에서 원본을 찾지 못했습니다)if form에서.is_valid () 함수 실행이 완료되면 auth 에 도착합니다login(request,form.get user()), 여기의form.get_user () 함수는 authenticate가 되돌려준user, 마지막authlogin 함수는 authenticate에서 되돌아오는user를 Request에 부여합니다.사용자,session에 썼습니다. 즉, 이번 요청 사이트의admin에 신분 탭이 있습니다.다음에 이 요청이 로그인이 필요한지 판단할 때 바로 Request를 보십시오.사용자가 존재하는지, 이 사용자가 합법적으로 로그인을 허용하면 ok입니다.구체적으로는django/contrib/admin/sites에 나타난다.py의login 함수,
@never_cache
def login(self, request, extra_context=None):
"""
Displays the login form for the given HttpRequest.
"""
if request.method == 'GET' and self.has_permission(request):
# Already logged-in, redirect to admin index
index_path = reverse('admin:index', current_app=self.name)
return HttpResponseRedirect(index_path)
。。。
브라우저 페이지에 만약 폼이 없다면 모든 요청은 get입니다. 우리가 우리의admin 사이트를 직접 요청할 때 get 방법을 사용하기 때문에self만 주목하십시오.has_permission(request): 이 판단은 이 함수에 대한 원본 코드가 다음과 같습니다.
def has_permission(self, request):
"""
Returns True if the given HttpRequest has permission to view
*at least one* page in the admin site.
"""
return request.user.is_active and request.user.is_staff
그러니까 리퀘스트만.user 객체의 isactive 그리고 isstaff, 그럼 바로 admin의 index로 돌아갑니다.html 인터페이스
그럼 또 하나의 의문이 있습니다, Request.사용자 도대체 언제 얻은 거예요?http://www.jianshu.com/writer#/notebooks/14133407/notes/14917548request 대상은 wsgi 응용 프로그램을 호출할 때 만든 WSGI Request 대상입니다. 처음에 이 대상은 http 요청 정보와 상하문 환경에 대한 봉인된 다음에django의middleware에 매개 변수로 전달하여 처리합니다. 인증이 필요한 프로젝트에는django를 설치해야 합니다.contrib.auth.middleware.Authentication Middleware, 이 middleware 때문에 Request 대상에user 속성이 생겼습니다.이것은django/contrib에서auth.middleware.py의 def processrequest 함수 설명도 알 수 있지만,
def process_request(self, request):
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
이제 본격적으로 Authentication Middleware에 가서 리퀘스트를 찾아보자.사용자, Authentication Middleware의 소스는 다음과 같습니다.
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request)) #
보셨습니까? 마지막 한마디는 Request를 만드는 것입니다.사용자 등록 정보.그러니까 리퀘스트.user는django 권한 검증 시스템의 기초입니다.그럼 이user 대상을 만드는 근거는 무엇일까요, 어떻게user를 만들었는지는 isactive 그리고 is스태프는요?계속 get사용자 함수:
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request) #
return request._cached_user
여기는auth에 중점을 두고 있습니다.get_사용자 (request), 원본 보기:django/contrib/auth/init.py
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 = _get_user_session_key(request) #
backend_path = request.session[BACKEND_SESSION_KEY] #
print "user_id: %s, backend_path: %s" %(user_id, backend_path)
except KeyError:
pass
else:
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id) #
print "user attr: ", dir(user)
# Verify the session
if hasattr(user, 'get_session_auth_hash'):
session_hash = request.session.get(HASH_SESSION_KEY)
session_hash_verified = session_hash and constant_time_compare(
session_hash,
user.get_session_auth_hash()
)
if not session_hash_verified:
request.session.flush()
user = None
return user or AnonymousUser()
여기서 우선 user 에 주목해 주세요.id = _get_user_session_key(request),backend_path = request.session[BACKEND SESSION KEY] 두 함수 모두 request에 사용됩니다.session, 그럼 Request.세션 어디서 왔어요?중간부품django.contrib.sessions.middleware.Authentication Middleware, 그리고 Authentication Middleware에서 Authentication Middleware가Session Middleware에 의존하고 settings의 설정에서INSTALLAPPS에서 django를 볼 수 있습니다.contrib.sessions.middleware.SessionMiddleware는django입니다.contrib.auth.middleware.Authentication Middleware, 마찬가지로 원본 코드를 다시 한 번 보고 assert hasattr(request,'session')에 중심을 두면 설명도 볼 수 있습니다.
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
그럼 구체적으로 Session Middleware는 뭘 했나요?우리는 원본 코드에서 볼 때 다음과 같다.
class SessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)
print "request.session._session: ", request.session._session
print "_session_cache: ", request.session._session_cache
COOKIES에서 세션을 획득함으로써키, 그리고django의session 데이터베이스에서 대응하는session 대상을 찾아 리퀘스트에 값을 부여합니다.세션, 이 세션 대상은 이 세션에 대응하는 로그인 사용자 id를 저장하기 때문에 마지막 get사용자 함수는 Request를 처리합니다.session은 사용자 id를 획득하여 해당하는 AUTH 로부터USER_MODEL이 지정한 데이터베이스에서 대응하는user 대상을 가져와 Request에 값을 부여합니다.사용자가 후속 권한 검증을 진행합니다.(session 및 cookies에 대해서는 다음을 참조하십시오.https://github.com/alsotang/node-lessons/tree/master/lesson16 http://mertensming.github.io/2016/10/19/cookie-session/)
지금, Authentication Middleware와 같은middleware의processRequest는 언제 호출되었습니까?django가 요청을 처리하는 프로세스를 잊지 마십시오.django의 요청 처리 프로세스는 먼저middleware 처리를 통해middleware가response로 되돌아오면 우리가 정의한url->view 처리 프로세스에 가지 않습니다.사실, 모든middleware에는 고정된 방법이 있고, 모든 요청의 처리 절차는Basehandler류의load 를 거친다middleware 함수, 이 함수는middleware에서 정의한 대응하는 함수를 고정된 함수 집합에 불러옵니다.
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
그리고 순서대로 요청을 처리한다.더 자세한 것은django/core/handlers/base 참조.py의 loadmiddleware 함수, 기본 코드는 다음과 같습니다.
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE (or the deprecated
MIDDLEWARE_CLASSES).
Must be called after the environment is fixed (see __call__ in subclasses).
"""
self._request_middleware = []
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
if settings.MIDDLEWARE is None:
warnings.warn(
"Old-style middleware using settings.MIDDLEWARE_CLASSES is "
"deprecated. Update your middleware and use settings.MIDDLEWARE "
"instead.", RemovedInDjango20Warning
)
handler = convert_exception_to_response(self._legacy_get_response)
for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if hasattr(mw_instance, 'process_request'):
self._request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)
else:
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if mw_instance is None:
raise ImproperlyConfigured(
'Middleware factory %s returned None.' % middleware_path
)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)
handler = convert_exception_to_response(mw_instance)
# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._middleware_chain = handler
SessionMiddleware가 요청이 들어왔을 때 프로세스를 호출합니다request에 빈session을 만들고 Authorization Middleware에 가서 seesion에 따라 사용자를 가져옵니다. 만약 가져오는 데 실패하면 익명 사용자를 만들고,response 흐름을 되돌릴 때Session Middleware의processresponse, 이 때 Request에 따라.사용자의 인증 상황은session을 저장하거나 삭제합니다.하지만 이곳에 사순환이 일어난 것 같다.만약 이 절차라면, 계속 익명 사용자가 되어서는 안 됩니까?첫 번째 유효한 사용자 검증은 어디에 두었을까요?개인적으로 이것은 프로젝트의 구체적인 배치에 달려 있다.session의 생성과 저장은 Request에 근거합니다.사용자가 하는 일이지만, 전체 요청 절차에서, 우리는 많은 곳에서 Request를 바꿀 수 있습니다.사용자, 그러나 대부분의 교체 작업은 우리가 url에 대응하는 View를 정의하는 것입니다. 예를 들어django가 자체로 가지고 있는 인증 로그인 인터페이스 프로세스에서 로그인 후auth 를 호출합니다.login(request,form.get user() 함수로 바꾸기;아니면django의rest-framework를 바탕으로 자신의 APIview를 정의할 때 프레임워크의Authorization을 호출합니다class는 사용자 인증을 하고 Request를 바꿉니다.user.
요약: 사용자가 Admin에 처음 로그인했을 때 Request.COOKIES에 sessionid가 없으므로 auth/init.py의 getuser 함수는 AnonymousUser () 대상을 되돌려줍니다. 그의 isstaff와 isactive는 모두false이기 때문에admin에 방문하면login, 즉 로그인 인터페이스로 바뀌고 사용자 이름과 비밀번호를 제출하여 로그인합니다. form에 있습니다.is_valid () 함수에서 제출한 사용자 이름과 비밀번호를 settings에서 지정한 AUTHENTICATIONBACKENDS에서 검증하고 성공하면 admin의 index로 다시 지정합니다.html인터페이스.다음에 admin 인터페이스를 다시 방문할 때 리퀘스트의 cookies에sessionid가 있기 때문에Session Middleware는 이sessionid에 따라session 데이터베이스에서 대응하는session의 값을 리퀘스트에 부여합니다.session, Authentication Middleware 처리 Request.session은 사용자, 즉 이번 방문에 대응하는 사용자를 가져와 Request에 값을 부여합니다.user는 다음에 판단하고 호출할 수 있습니다.login 함수에서 Request.user 진행 isstaff와 isactive 판단, 최종 결정은admin의 index로 바꿉니다.html 아니면 로그인 인터페이스.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.