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验证,我建议你使用第二个禁用方法,禁的不折不扣的。若是你使用的是第一个方法,你会发现,当你使用admin
、xadmin
或其余第三方关于认证的插件时,CSRF
机制有时候仍是会蹦出来作怪。为何?为何禁了没有效果?
我在使用xadmin
的时候就遇到这种状况,分明注释了django.middleware.csrf.CsrfViewMiddleware
,仍是会提示CSRF
验证失败(失败缘由我就不过多解释了,不一样人遇到的状况不同的,我这里是由于域名作了二次代理),以登陆为例,xadmin
部分源码以下:html
# 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()
为xadmin
下class LoginView()
内的方法;login.as_view()
为django
原生的登陆验证类
django
原生的登陆验证类源码以下: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)
注意这行代码@method_decorator(csrf_protect)
在这里你要知道的是,装饰器csrf_protect
的做用是进行CSRF
验证
因此,即便你注释了django.middleware.csrf.CsrfViewMiddleware
,在这里通过装饰器csrf_protect
仍是会再次进行CSRF
验证。
真相终于大白了。
接下说说,第二种禁用CSRF
方法web
经过查看@csrf_protect
源码(就不贴上来了)会发现,内部实现是,对class CsrfViewMiddleware
进行了实例化,而后依次调用了中间件中def process_request()
、def process_view()
等方法,其中,CsrfViewMiddleware.process_view()
,是进行CSRF
验证的逻辑,源码以下:django
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
。
致此,完!cookie