Django 全局徹底禁用CSRF機制

寫在以前
  • 只講禁用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部分源碼以下: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()xadminclass 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

相關文章
相關標籤/搜索