溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何在Django中實現(xiàn)CSRF認證

發(fā)布時間:2021-06-01 17:44:39 來源:億速云 閱讀:234 作者:Leah 欄目:開發(fā)技術(shù)

如何在Django中實現(xiàn)CSRF認證?針對這個問題,這篇文章詳細介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

1.csrf原理

csrf要求發(fā)送post,put或delete請求的時候,是先以get方式發(fā)送請求,服務(wù)端響應(yīng)時會分配一個隨機字符串給客戶端,客戶端第二次發(fā)送post,put或delete請求時攜帶上次分配的隨機字符串到服務(wù)端進行校驗

2.Django中的CSRF中間件

首先,我們知道Django中間件作用于整個項目。

在一個項目中,如果想對全局所有視圖函數(shù)或視圖類起作用時,就可以在中間件中實現(xiàn),比如想實現(xiàn)用戶登錄判斷,基于用戶的權(quán)限管理(RBAC)等都可以在Django中間件中來進行操作

Django內(nèi)置了很多中間件,其中之一就是CSRF中間件

MIDDLEWARE_CLASSES = [
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

上面第四個就是Django內(nèi)置的CSRF中間件

3.Django中間件的執(zhí)行流程

Django中間件中最多可以定義5個方法

  • process_request

  • process_response

  • process_view

  • process_exception

  • process_template_response

Django中間件的執(zhí)行順序

1.請求進入到Django后,會按中間件的注冊順序執(zhí)行每個中間件中的process_request方法
    如果所有的中間件的process_request方法都沒有定義return語句,則進入路由映射,進行url匹配
    否則直接執(zhí)行return語句,返回響應(yīng)給客戶端

2.依次按順序執(zhí)行中間件中的process_view方法
    如果某個中間件的process_view方法沒有return語句,則根據(jù)第1步中匹配到的URL執(zhí)行對應(yīng)的視圖函數(shù)或視圖類
    如果某個中間件的process_view方法中定義了return語句,則后面的視圖函數(shù)或視圖類不會執(zhí)行,程序會直接返回

3.視圖函數(shù)或視圖類執(zhí)行完成之后,會按照中間件的注冊順序逆序執(zhí)行中間件中的process_response方法
    如果中間件中定義了return語句,程序會正常執(zhí)行,把視圖函數(shù)或視圖類的執(zhí)行結(jié)果返回給客戶端
    否則程序會拋出異常

4.程序在視圖函數(shù)或視圖類的正常執(zhí)行過程中
    如果出現(xiàn)異常,則會執(zhí)行按順序執(zhí)行中間件中的process_exception方法
    否則process_exception方法不會執(zhí)行
    如果某個中間件的process_exception方法中定義了return語句,則后面的中間件中的process_exception方法不會繼續(xù)執(zhí)行了

5.如果視圖函數(shù)或視圖類中使用render方法來向客戶端返回數(shù)據(jù),則會觸發(fā)中間件中的process_template_response方法

4.Django CSRF中間件的源碼解析

Django CSRF中間件的源碼

class CsrfViewMiddleware(MiddlewareMixin):

 def _accept(self, request):
  request.csrf_processing_done = True
  return None

 def _reject(self, request, reason):
  logger.warning(
   'Forbidden (%s): %s', reason, request.path,
   extra={
    'status_code': 403,
    'request': request,
   }
  )
  return _get_failure_view()(request, reason=reason)

 def _get_token(self, request):
  if settings.CSRF_USE_SESSIONS:
   try:
    return request.session.get(CSRF_SESSION_KEY)
   except AttributeError:
    raise ImproperlyConfigured(
     'CSRF_USE_SESSIONS is enabled, but request.session is not '
     'set. SessionMiddleware must appear before CsrfViewMiddleware '
     'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '')
    )
  else:
   try:
    cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
   except KeyError:
    return None

   csrf_token = _sanitize_token(cookie_token)
   if csrf_token != cookie_token:
    # Cookie token needed to be replaced;
    # the cookie needs to be reset.
    request.csrf_cookie_needs_reset = True
   return csrf_token

 def _set_token(self, request, response):
  if settings.CSRF_USE_SESSIONS:
   request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
  else:
   response.set_cookie(
    settings.CSRF_COOKIE_NAME,
    request.META['CSRF_COOKIE'],
    max_age=settings.CSRF_COOKIE_AGE,
    domain=settings.CSRF_COOKIE_DOMAIN,
    path=settings.CSRF_COOKIE_PATH,
    secure=settings.CSRF_COOKIE_SECURE,
    httponly=settings.CSRF_COOKIE_HTTPONLY,
   )
   patch_vary_headers(response, ('Cookie',))

 def process_request(self, request):
  csrf_token = self._get_token(request)
  if csrf_token is not None:
   # Use same token next time.
   request.META['CSRF_COOKIE'] = csrf_token

 def process_view(self, request, callback, callback_args, callback_kwargs):
  if getattr(request, 'csrf_processing_done', False):
   return None

  if getattr(callback, 'csrf_exempt', False):
   return None

  if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
   if getattr(request, '_dont_enforce_csrf_checks', False):
    return self._accept(request)

   if request.is_secure():
    referer = force_text(
     request.META.get('HTTP_REFERER'),
     strings_only=True,
     errors='replace'
    )
    if referer is None:
     return self._reject(request, REASON_NO_REFERER)

    referer = urlparse(referer)

    if '' in (referer.scheme, referer.netloc):
     return self._reject(request, REASON_MALFORMED_REFERER)

    if referer.scheme != 'https':
     return self._reject(request, REASON_INSECURE_REFERER)

    good_referer = (
     settings.SESSION_COOKIE_DOMAIN
     if settings.CSRF_USE_SESSIONS
     else settings.CSRF_COOKIE_DOMAIN
    )
    if good_referer is not None:
     server_port = request.get_port()
     if server_port not in ('443', '80'):
      good_referer = '%s:%s' % (good_referer, server_port)
    else:
     good_referer = request.get_host()

    good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
    good_hosts.append(good_referer)

    if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
     reason = REASON_BAD_REFERER % referer.geturl()
     return self._reject(request, reason)

   csrf_token = request.META.get('CSRF_COOKIE')
   if csrf_token is None:
    return self._reject(request, REASON_NO_CSRF_COOKIE)

   request_csrf_token = ""
   if request.method == "POST":
    try:
     request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
    except IOError:
     pass

   if request_csrf_token == "":
    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')

   request_csrf_token = _sanitize_token(request_csrf_token)
   if not _compare_salted_tokens(request_csrf_token, csrf_token):
    return self._reject(request, REASON_BAD_TOKEN)

  return self._accept(request)

 def process_response(self, request, response):
  if not getattr(request, 'csrf_cookie_needs_reset', False):
   if getattr(response, 'csrf_cookie_set', False):
    return response

  if not request.META.get("CSRF_COOKIE_USED", False):
   return response

  self._set_token(request, response)
  response.csrf_cookie_set = True
  return response

從上面的源碼中可以看到,CsrfViewMiddleware中間件中定義了process_request,process_view和process_response三個方法

先來看process_request方法

def _get_token(self, request): 
 if settings.CSRF_USE_SESSIONS: 
  try: 
   return request.session.get(CSRF_SESSION_KEY) 
  except AttributeError: 
   raise ImproperlyConfigured( 
    'CSRF_USE_SESSIONS is enabled, but request.session is not ' 
 'set. SessionMiddleware must appear before CsrfViewMiddleware ' 'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '') 
   ) 
 else: 
  try: 
   cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] 
  except KeyError: 
   return None 
 
 csrf_token = _sanitize_token(cookie_token) 
  if csrf_token != cookie_token: 
   # Cookie token needed to be replaced; 
 # the cookie needs to be reset. request.csrf_cookie_needs_reset = True 
 return csrf_token

def process_request(self, request): 
  csrf_token = self._get_token(request) 
  if csrf_token is not None: 
   # Use same token next time. 
  request.META['CSRF_COOKIE'] = csrf_token

從Django項目配置文件夾中讀取 CSRF_USE_SESSIONS 的值,如果獲取成功,則 從session中讀取CSRF_SESSION_KEY的值 ,默認為 '_csrftoken' ,如果沒有獲取到 CSRF_USE_SESSIONS 的值,則從發(fā)送過來的請求中獲取 CSRF_COOKIE_NAME 的值,如果沒有定義則返回None。

再來看process_view方法

在process_view方法中,先檢查視圖函數(shù)是否被 csrf_exempt 裝飾器裝飾,如果視圖函數(shù)沒有被csrf_exempt裝飾器裝飾,則程序繼續(xù)執(zhí)行,否則返回None。接著從request請求頭中或者cookie中獲取攜帶的token并進行驗證,驗證通過才會繼續(xù)執(zhí)行與URL匹配的視圖函數(shù),否則就返回 403 Forbidden 錯誤。

實際項目中,會在發(fā)送POST,PUT,DELETE,PATCH請求時,在提交的form表單中添加

{% csrf_token %}

即可,否則會出現(xiàn)403的錯誤

如何在Django中實現(xiàn)CSRF認證

5.csrf_exempt裝飾器和csrf_protect裝飾器

5.1 基于Django FBV

在一個項目中,如果注冊起用了 CsrfViewMiddleware 中間件,則項目中所有的視圖函數(shù)和視圖類在執(zhí)行過程中都要進行CSRF驗證。

此時想使某個視圖函數(shù)或視圖類不進行CSRF驗證,則可以使用 csrf_exempt 裝飾器裝飾不想進行CSRF驗證的視圖函數(shù)

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt 
def index(request): 
 pass

也可以把csrf_exempt裝飾器直接加在URL路由映射中,使某個視圖函數(shù)不經(jīng)過CSRF驗證

from django.views.decorators.csrf import csrf_exempt 
 
from users import views 
 
urlpatterns = [ 
 url(r'^admin/', admin.site.urls), 
 url(r'^index/',csrf_exempt(views.index)), 
]

同樣的,如果在一個Django項目中,沒有注冊起用 CsrfViewMiddleware 中間件,但是想讓某個視圖函數(shù)進行CSRF驗證,則可以使用 csrf_protect 裝飾器

csrf_protect裝飾器的用法跟csrf_exempt裝飾器用法相同 ,都可以加上視圖函數(shù)上方裝飾視圖函數(shù)或者在URL路由映射中直接裝飾視圖函數(shù)

from django.views.decorators.csrf import csrf_exempt 

@csrf_protect 
def index(request): 
 pass

或者

from django.views.decorators.csrf import csrf_protect 
 
from users import views 
 
urlpatterns = [ 
 url(r'^admin/', admin.site.urls), 
 url(r'^index/',csrf_protect(views.index)), 
]

5.1 基于Django CBV

上面的情況是基于Django FBV的,如果是基于Django CBV,則不可以直接加在視圖類的視圖函數(shù)中了

此時有三種方式來對Django CBV進行CSRF驗證或者不進行CSRF驗證

方法一,在視圖類中定義dispatch方法,為dispatch方法加csrf_exempt裝飾器

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

class UserAuthView(View):

 @method_decorator(csrf_exempt)
 def dispatch(self, request, *args, **kwargs):
  return super(UserAuthView,self).dispatch(request,*args,**kwargs)

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

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

 def put(self,request,*args,**kwargs):
  pass

 def delete(self,request,*args,**kwargs):
  pass

方法二:為視圖類上方添加裝飾器

@method_decorator(csrf_exempt,name='dispatch')
class UserAuthView(View):
 def get(self,request,*args,**kwargs):
  pass

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

 def put(self,request,*args,**kwargs):
  pass

 def delete(self,request,*args,**kwargs):
  pass

方式三:在url.py中為類添加裝飾器

from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
 url(r'^admin/', admin.site.urls),
 url(r'^auth/', csrf_exempt(views.UserAuthView.as_view())),
]

關(guān)于如何在Django中實現(xiàn)CSRF認證問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI