Django自定义认证系统原理及源码分析解读

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django自定义认证系统原理及源码分析解读相关的知识,希望对你有一定的参考价值。

疑问

Django在​​如何自定义用户登录认证系统的时候​​,大家都会里面立马说 自定义一个 或者多个backend,比如通过账号+密码、邮箱+密码,邮箱+验证码、手机号+短信验证码等等。 然后设置 在settings中配置一个 ​​AUTHENTICATION_BACKENDS​​就行。

但是为什么要这么做呢? 原理是什么呢?

今天就带大家分析一波Django的认证相关的源码逻辑,告诉你为什么要这么做。

关于认证登录

结论预告 >>>>

Django 默认的认证保持功能主要是通过 用户名+密码 发送给后端之后,会先去通过 authenticate 函数验证 用户名和密码是否正确; 如果正确则进行 login 登录,login字后会把对应的用户 user 存入到session中。并且request.user 为当前 user(而不是默认的 AnonymousUser)

所以Django的认证核心是两步

# 验证用户
user = auth.authenticate(username=username, password=password)
# 然后登录
auth.login(request, user)

源码解读

对于Django自带的管理后台的登录,首先要明确几个点

1、自定义的应用都是通过 ​​admin.site.register()​​ 注册到 Admin后台去的

2、对于Django自带的 ​​admin​​ 应用,它也是把自己注册到 AdminSite 中去的 (源码位置: django/contrib/admin.sites.py 中 AdminSite类的 \\_\\init_() 方法中)

Django新增项目之后,在项目目录下的​​urls.py​​ 文件配置的所有项目的路由地址入口,后续新增的应用的也都是通过这里进行include配置。

# proj/urls.py
urlpatterns = [
path(admin/, admin.site.urls),
]

Django自带后台的登录地址 ​​http://127.0.0.1:8000/admin/login​​ 所以登录逻辑入口就是 admin.site.urls

定位登录入口

从源码 ​​django/contrib/admin.sites.py​​ 中

class AdminSite:
... ...
def get_urls(self):
... ...
# Admin-site-wide views.
urlpatterns = [
path(, wrap(self.index), name=index),
path(login/, self.login, name=login),
... ...
]

@property
def urls(self):
return self.get_urls(), admin, self.name

... ...
@never_cache
def login(self, request, extra_context=None):
... ...
from django.contrib.admin.forms import AdminAuthenticationForm
from django.contrib.auth.views import LoginView
... ...
defaults =
extra_context: context,
# 这里 self.login_form为空, 所以 authentication_form是 AdminAuthenticationForm
authentication_form: self.login_form or AdminAuthenticationForm,
template_name: self.login_template or admin/login.html,

request.current_app = self.name
return LoginView.as_view(**defaults)(request)

admin.site.urls 最终调用 get_urls() 方法, 在该方法中定义了 ​​login​​ 路由,对应的视图函数是​​self.login​​。

然后查阅login函数发现它返回的是 ​​LoginView 类视图​​, 来源于 ​​django.contrib.auth.views​

另外这里也需要注意下 ​​django.contrib.admin.forms.AdminAuthenticationForm​​ 因为最后实际登录的时候用到的Form表单就是这个

重点看LoginView视图

# django/contrib/auth/views.py
class LoginView(SuccessURLAllowedHostsMixin, FormView):
"""
Display the login form and handle the login action.
"""
form_class = AuthenticationForm
authentication_form = None
redirect_field_name = REDIRECT_FIELD_NAME
template_name = registration/login.html
redirect_authenticated_user = False
extra_context = None

@method_decorator(sensitive_post_parameters())
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if self.redirect_authenticated_user and self.request.user.is_authenticated:
redirect_to = self.get_success_url()
if redirect_to == self.request.path:
raise ValueError(
"Redirection loop for authenticated user detected. Check that "
"your LOGIN_REDIRECT_URL doesnt point to a login page."
)
return HttpResponseRedirect(redirect_to)
return super().dispatch(request, *args, **kwargs)

def get_success_url(self):
url = self.get_redirect_url()
return url or resolve_url(settings.LOGIN_REDIRECT_URL)

def get_redirect_url(self):
"""Return the user-originating redirect URL if its safe."""
redirect_to = self.request.POST.get(
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name, )
)
url_is_safe = url_has_allowed_host_and_scheme(
url=redirect_to,
allowed_hosts=self.get_success_url_allowed_hosts(),
require_https=self.request.is_secure(),
)
return redirect_to if url_is_safe else

def get_form_class(self):
return self.authentication_form or self.form_class

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs[request] = self.request
return kwargs

def form_valid(self, form):
"""Security check complete. Log the user in."""
auth_login(self.request, form.get_user())
return HttpResponseRedirect(self.get_success_url())

这里 SuccessURLAllowedHostsMixin 可以先忽略(它是判断允许那些主机来访问URL),

LoginView继承自 FormView, FormView 继承自 TemplateResponseMixin 和 BaseFormView,而BaseFormView又继承自FormMixin 和 ProcessFormView

# django/views/generic/edit.py
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())

def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if its valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)



class BaseFormView(FormMixin, ProcessFormView):
"""A base view for displaying a form."""


class FormView(TemplateResponseMixin, BaseFormView):
"""A view for displaying a form and rendering a template response."""

同样的TemplateResponseMixin是定义返回结果格式的一个Mixin,可以先忽略。

定位Post

我们知道 login 最终发送的是一个 post 请求。

对于Django 类视图的请求解析路径大概流程是:

1) 通过XxxxView.as_view() 最终到 View 类(位于 django/views/generic/base.py)中 请求 as_view 方法

2)as_view方法中调用 setup() 方法, ​​setup() 方法初始化 request/args/kwargs 参数​​ 这里划个​​**重点**​​,

3)然后在as_view方法中继续调用 dispatch() 方法,该方法获取handler,这个handler就是最终调用方法

http_method_names = [get, post, put, patch, delete, head, options, trace]

所以通过继承关系,知道最终 post 调用的是 ​​ProcessFormView类中的方法​

这里先 获取 form 然后判断 form的有效性,通过之后执行​​form_valid(form)​

表单详解

然后我们拆开来分析上面的简单的三个步骤

获取form表单

通过上面的源码知道了 BaseFormView 继承 FormMixin 和 ProcessFormView 两个类, 在 ProcessFormView 中的post中使用了 self.get_form() 方法, 该方法其实位于 FormMixin类

# django/views/generic/edit.py
class FormMixin(ContextMixin):
"""Provide a way to show and handle a form in a request."""
initial =
form_class = None
success_url = None
prefix = None
... ...

def get_form_class(self):
"""Return the form class to use."""
return self.form_class

def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())

特别注意

以上是关于Django自定义认证系统原理及源码分析解读的主要内容,如果未能解决你的问题,请参考以下文章

Django(64)频率认证源码分析与自定义频率认证

drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用

django的RBAC认证z;自定义auth_user表;认证组件权限组件源码分析;认证组件;权限组件

Django(63)drf权限源码分析与自定义权限

框架之认证接口的源码分析及自定义接口的使用

python/django models中自定义用户认证及django admin自定义