Django Rest Framework 删除 csrf

我知道有关于姜戈休息框架的答案,但我找不到解决我的问题的方法。

我有一个应用程序,它具有身份验证和一些功能。 我添加了一个新的应用程序,它使用 Django 休息框架。我只想在这个应用程序中使用库。另外,我想发出 POST 请求,我总是收到这样的回复:

{
"detail": "CSRF Failed: CSRF token missing or incorrect."
}

我有以下密码:

# urls.py
from django.conf.urls import patterns, url




urlpatterns = patterns(
'api.views',
url(r'^object/$', views.Object.as_view()),
)


# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt




class Object(APIView):


@csrf_exempt
def post(self, request, format=None):
return Response({'received data': request.data})

我希望在不影响当前应用程序的情况下添加 API。 所以我的问题是,我怎样才能只为这个应用程序禁用 CSRF?

126632 次浏览

如果您不想使用基于会话的身份验证,可以从 REST _ AUTHENTATION _ CLASSES 中删除 Session Authentication,这将自动删除所有基于 csrf 的问题。但在这种情况下,可浏览的 apis 可能无法工作。

此外,即使使用会话身份验证,也不应该出现此错误。您应该对 API 使用自定义身份验证(如 TokenAuthentication) ,并确保在请求中连同身份验证令牌一起发送 Accept:application/jsonContent-Type:application/json(前提是您使用 json)。

注意: 从安全角度来看,禁用 CSRF 是不安全的。请根据您的判断使用以下方法。

为什么会发生这种错误?

这是由于 DRF 使用的默认 SessionAuthentication方案造成的。DRF 的 SessionAuthentication使用 Django 的会话框架进行身份验证,这需要检查 CSRF。

当您在视图/视图集中没有定义任何 authentication_classes时,DRF 使用这个身份验证类作为默认类。

'DEFAULT_AUTHENTICATION_CLASSES'= (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),

由于 DRF 需要同时支持对相同视图的基于会话和非会话的身份验证,因此它只对经过身份验证的用户执行 CSRF 检查。这意味着只有经过身份验证的请求才需要 CSRF 令牌,而匿名请求可以在没有 CSRF 令牌的情况下发送。

如果在 SessionAuthentication 中使用 AJAX 样式的 API,则需要为任何“不安全”的 HTTP 方法调用(如 PUT, PATCH, POST or DELETE请求)包含有效的 CSRF 令牌。

那怎么办?

现在,要禁用 csrf 检查,您可以创建一个自定义身份验证类 CsrfExemptSessionAuthentication,它扩展了默认的 SessionAuthentication类。在这个身份验证类中,我们将覆盖实际 SessionAuthentication内部发生的 enforce_csrf()检查。

from rest_framework.authentication import SessionAuthentication, BasicAuthentication


class CsrfExemptSessionAuthentication(SessionAuthentication):


def enforce_csrf(self, request):
return  # To not perform the csrf check previously happening

在您看来,那么您可以将 authentication_classes定义为:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

这应该可以处理 csrf 错误。

我遇到了同样的问题。我跟随这个 参考文献,它工作了。 解决方案是创建一个中间件

在应用程序中添加 disable.py 文件(在我的例子中是“ myapp”)

class DisableCSRF(object):
def process_request(self, request):
setattr(request, '_dont_enforce_csrf_checks', True)

并将中间件添加到 MIDDLEWARE _ CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

更简单的解决办法:

在 views.py 中,使用 django-braces 的 CsrfExemptMixinauthentication_classes:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from braces.views import CsrfExemptMixin




class Object(CsrfExemptMixin, APIView):
authentication_classes = []


def post(self, request, format=None):
return Response({'received data': request.data})

如果对应用程序使用独占虚拟环境,则可以在不影响其他应用程序的情况下使用以下方法。

之所以会发生这种情况,是因为 rest_framework/authentication.pySessionAuthentication类的 authenticate方法中有这段代码:

self.enforce_csrf(request)

如果不想检查 CSRF,可以修改 Request类,使其具有名为 csrf_exempt的属性,并在各自的 View 类中将其初始化为 True。例如:

下一步,修改上述代码如下:

if not request.csrf_exempt:
self.enforce_csrf(request)

有一些相关的变化,你必须做到这一点在 Request

对于那些没有找到有用答案的人。是的,如果不使用 SessionAuthentication AUTHENTATION CLASS,DRF 会自动移除 CSRF 保护,例如,许多开发人员只使用 JWT:

'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),

但是,问题 CSRF not set可能由于其他原因而发生,例如,您没有正确地将路径添加到您的视图:

url(r'^api/signup/', CreateUserView),  # <= error! DRF cant remove CSRF because it is not as_view that does it!

而不是

url(r'^api/signup/', CreateUserView.as_view()),

我的解决方案显示打击。只是装饰我的类。

from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
@method_decorator(basic_auth_required(
target_test=lambda request: not request.user.is_authenticated
), name='dispatch')
class GenPedigreeView(View):
pass

修改 urls.py

如果在 urls.py 中管理路由,则可以用 CSRF _  免除()包裹所需的路由,以便將它們排除在 CSRF 認證中間件之外。

import views


from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt




urlpatterns = patterns('',
url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
...
)

或者,作为装饰者 有些人可能发现使用@csrf _ free 装饰符更适合他们的需要

比如说,

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse


@csrf_exempt
def my_view(request):
return HttpResponse('Hello world')

应该完成任务!

这也可能是在 DNS 重新绑定攻击期间的一个问题。

在 DNS 更改之间,这也可能是一个因素。等待 DNS 完全刷新将解决这个问题,如果它在 DNS 问题/更改之前工作。

我尝试了上面的几个答案,觉得创建一个单独的类有点过了。

作为参考,我在尝试将基于函数的视图方法更新为用户注册的基于类的视图方法时遇到了这个问题。

当使用基于类的视图(CBV)和 Django RestFramework (DRF)时,从 ApiView 类继承,并将 mission _ classes 和 entication _ class 设置为空元组。请看下面的例子。

class UserRegistrationView(APIView):


permission_classes = ()
authentication_classes = ()


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


# rest of your code here

在使用 REST API POST 时,缺少 X-CSRFToken 请求头可能会导致该错误。 Django docs 提供了从 JS 获取和设置 CSRF 令牌值的示例代码。

正如上面的答案所指出的,CSRF 检查在使用 SessionAuthentication 时发生。另一种方法是使用 TokenAuthentication,但是请记住,它应该放在 REST _ FRAMEWORK 设置的 DEFAULT _ AUTHENTATION _ CLASSES 列表的第一位。

您需要添加这个来防止默认的会话身份验证: (setings.py)

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}

然后: (views.py)

from rest_framework.permissions import AllowAny


class Abc(APIView):
permission_classes = (AllowAny,)


def ...():

移除 CSRF 检查并不总是唯一(或最好)的解决方案。实际上,它是 SessionAuthentication的一个重要安全机制。

我在尝试使用 JWT 进行身份验证和执行 POST 请求时遇到了同样的问题。

我最初的设置是这样的:

REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.SessionAuthentication",
"django_cognito_jwt.JSONWebTokenAuthentication",
),
...
}

当在列表中首先检查 SessionAuthentication时,会引发 CSRF 错误。我的解决方案非常简单,只需更改总是首先检查 JWT auth 的顺序。像这样:

    "DEFAULT_AUTHENTICATION_CLASSES": (
"django_cognito_jwt.JSONWebTokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
),

最后,我只在 django 管理面板中使用 SessionAuthentication,99% 的请求都发送给使用 JWT auth 的 API。

你需要完全确定,你想关闭 CSRF 保护。

  1. 创建文件 authentication.py并将其放置在项目中的任何位置。
  2. 将以下代码放入文件:
from rest_framework.authentication import SessionAuthentication




class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
pass


  1. 当您想要向您的视图发出 POSTPUTPATCHDELETE请求时,请确保您已经将 SessionAuthentication从新文件更改为 SessionCsrfExemptAuthentication。查看示例:
    @api_view(["POST"])
@authentication_classes([SessionCsrfExemptAuthentication])
@permission_classes([IsAuthenticated])
def some_view(request) -> "Response":
# some logic here
return Response({})

这个技巧允许您重写方法(传递) enforce_csrf,并且新的会话身份验证类将跳过 CSRF 检查。

Something

对我来说,使用 django 3.1.5django rest framework 3.12的解决方案更容易。

我碰巧在 views.py文件中定义了这两个方法:

@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def create_transaction(request):
return Response(status=status.HTTP_200_OK)


def create_transaction(initial_data):
pass

在我的 urls.py频道:

urlpatterns = [
path('transaction', views.create_transaction, name='transaction'),
]

Django 选择了最新的并抛出了错误,将其中一个重命名就解决了这个问题。

下面的代码将删除对 CSRF 的需求。即使是非用户也可以发送请求。

from typing import List, Any


class Object(APIView):
authentication_classes: List = []
permission_classes: List[Any] = [AllowAny]


...
...