寫在之前
- 只講禁用
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部分原始碼如下:
# 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原生的登錄驗證類原始碼如下:
# 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,
致此,完!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230168.html
標籤:Python
