Django全局完全禁用CSRF机制[Python基础]

python

写在之前

  • 只讲禁用CSRF方法,不讲CSRF讲理
  • 会记录Django中关于CSRF常用的一些方法和类
  • 以了解Django中间件为前提, 看以下内容

禁用方法

  • 最简单也是网上推荐最多的方法,找到settings.py => MIDDLEWARE配置项 => 修改如下

MIDDLEWARE = [

...

"django.middleware.common.CommonMiddleware",

# "django.middleware.csrf.CsrfViewMiddleware", # 注释掉此行代码即可

"django.contrib.auth.middleware.AuthenticationMiddleware",

...

]

  • 我推荐的写法,也是全局禁用最有效的方法,自定义中间件 middleware.py, 如下

# tools/middleware.py

from django.utils.deprecation import MiddlewareMixin

logger = logging.getLogger(__name__)

class CloseCsrfMiddleware(MiddlewareMixin):

def process_request(self, request):

request.csrf_processing_done = True # csrf处理完毕

logger.info("csrf全局禁用")

讲解

在上面两个方法中,如果你的目的是想全局禁用CSRF验证,我建议你使用第二个禁用方法,禁的彻彻底底的。如果你使用的是第一个方法,你会发现,当你使用adminxadmin或其他第三方关于认证的插件时,CSRF机制有时候还是会蹦出来做怪。为什么?为什么禁了没有效果?

我在使用xadmin的时候就遇到这种情况,分明注释了django.middleware.csrf.CsrfViewMiddleware,还是会提示CSRF验证失败(失败原因我就不过多解释了,不同人遇到的情况不一样的,我这里是因为域名做了二次代理),以登录为例,xadmin部分源码如下:

# xadmin/view/website.py

...

from django.contrib.auth.views import LoginView as login

...

class LoginView(BaseAdminView):

...

@never_cache

def get(self, request, *args, **kwargs):

context = self.get_context()

helper = FormHelper()

helper.form_tag = False

helper.include_media = False

context.update({

"title": self.title,

"helper": helper,

"app_path": request.get_full_path(),

REDIRECT_FIELD_NAME: request.get_full_path(),

})

defaults = {

"extra_context": context,

# "current_app": self.admin_site.name,

"authentication_form": self.login_form or AdminAuthenticationForm,

"template_name": self.login_template or "xadmin/views/login.html",

}

self.update_params(defaults)

# return login(request, **defaults)

return login.as_view(**defaults)(request)

@never_cache

def post(self, request, *args, **kwargs):

return self.get(request)

...

xadmin登录时,后台方法调用如下:
def post() => def get() => login.as_view(); 其中, def post()def get()xadminclass LoginView()内的方法;login.as_view()django原生的登录验证类

django原生的登录验证类源码如下:

# django/crontrab/auth/view.py

class LoginView(SuccessURLAllowedHostsMixin, FormView):

...

@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 doesn"t point to a login page."

)

return HttpResponseRedirect(redirect_to)

return super().dispatch(request, *args, **kwargs)

注意这行代码@method_decorator(csrf_protect)

在这里你要知道的是,装饰器csrf_protect的作用是进行CSRF验证

所以,即使你注释了django.middleware.csrf.CsrfViewMiddleware,在这里经过装饰器csrf_protect还是会再次进行CSRF验证。

真相终于大白了。

接下说说,第二种禁用CSRF方法

通过查看@csrf_protect源码(就不贴上来了)会发现,内部实现是,对class CsrfViewMiddleware进行了实例化,然后依次调用了中间件中def process_request()def process_view()等方法,其中,CsrfViewMiddleware.process_view(),是进行CSRF验证的逻辑,源码如下:

class CsrfViewMiddleware(MiddlewareMixin):

...

def process_view(self, request, callback, callback_args, callback_kwargs):

# 注意csrf_processing_done变量,这个变量很关键

# 这个变量目的是记录在本次请求中是否已经进行过CSRF校验

# 如果已经校验过了,就不再走下面的验证逻辑了。

if getattr(request, "csrf_processing_done", False):

return None

# 这一步是查看被调用的def view()方法是否加了@csrf_exempt装饰器

# 如果加了,就不再走下面的验证逻辑了。

# Wait until request.META["CSRF_COOKIE"] has been manipulated before

# bailing out, so that get_token still works

if getattr(callback, "csrf_exempt", False):

return None

# 下面就是CSRF的验证逻辑了

# Assume that anything not defined as "safe" by RFC7231 needs protection

if request.method not in ("GET", "HEAD", "OPTIONS", "TRACE"):

if getattr(request, "_dont_enforce_csrf_checks", False):

# Mechanism to turn off CSRF checks for test suite.

# It comes after the creation of CSRF cookies, so that

# everything else continues to work exactly the same

# (e.g. cookies are sent, etc.), but before any

# branches that call reject().

return self._accept(request)

...

如上所示, 第二种禁用CSRF方法原理就是, 设置request.csrf_processing_done=True

致此,完!

以上是 Django全局完全禁用CSRF机制[Python基础] 的全部内容, 来源链接: utcz.com/z/530856.html

回到顶部