写在之前
- 只讲禁用
CSRF
方法,不讲CSRF
讲理 - 会记录
Django
中关于CSRF
常用的一些方法和类 - 以了解
Django
中间件为前提, 看以下内容
禁用方法
- 最简单也是网上推荐最多的方法,找到
settings.py => MIDDLEWARE
配置项 => 修改如下
<code class="language-python"> MIDDLEWARE = [ ... "<a href="https://www.gaodaima.com/tag/django" title="查看更多关于django的文章" target="_blank">django</a>.<a href="https://www.gaodaima.com/tag/middleware" title="查看更多关于middleware的文章" target="_blank">middleware</a>.common.CommonMiddleware", # "django.middleware.<a href="https://www.gaodaima.com/tag/csrf" title="查看更多关于csrf的文章" target="_blank">csrf</a>.CsrfViewMiddleware", # 注释掉此行代码即可 "django.contrib.auth.middleware.AuthenticationMiddleware", ... ] </code>
www#gaodaima.com来源gao@daima#com搞(%代@#码网搞代码
- 我推荐的写法,也是全局禁用最有效的方法,自定义中间件
middleware.py
, 如下
<code class="language-python"># 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全局禁用") </code>
讲解
在上面两个方法中,如果你的目的是想全局禁用CSRF验证,我建议你使用第二个禁用方法,禁的彻彻底底的。如果你使用的是第一个方法,你会发现,当你使用admin
、xadmin
或其他第三方关于认证的插件时,CSRF
机制有时候还是会蹦出来做怪。为什么?为什么禁了没有效果?
我在使用xadmin
的时候就遇到这种情况,分明注释了django.middleware.csrf.CsrfViewMiddleware
,还是会提示CSRF
验证失败(失败原因我就不过多解释了,不同人遇到的情况不一样的,我这里是因为域名做了二次代理),以登录为例,xadmin
部分源码如下:
<code class="language-python"># 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) ... </code>
xadmin
登录时,后台方法调用如下:def post()
=> def get()
=> login.as_view()
; 其中, def post()
、def get()
为xadmin
下class LoginView()
内的方法;login.as_view()
为django
原生的登录验证类
django
原生的登录验证类源码如下:
<code class="language-python"># 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) </code>
注意这行代码@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
验证的逻辑,源码如下:
<code class="language-python">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) ... </code>
如上所示, 第二种禁用CSRF
方法原理就是, 设置request.csrf_processing_done=True
。
致此,完!