【问题标题】:Django @csrf_exempt not working in class ViewDjango @csrf_exempt 在类视图中不起作用
【发布时间】:2017-06-02 05:41:18
【问题描述】:

我在 Django 1.9 中有一个使用 SessionMiddleware 的应用程序。我想在同一个项目中为这个应用程序创建一个 API,但是在执行 POST 请求时,@csrf_exempt 注释不起作用。

我正在做的请求抛出 Postman,这就是我目前所拥有的:

settings.py

MIDDLEWARE_CLASSES = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'a9.utils.middleware.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'a9.core.access.middleware.AccessMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',    
]

OAUTH2_PROVIDER = {
    # this is the list of available scopes
    'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'}
}

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
)
CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
        #'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
        #'rest_framework.authentication.TokenAuthentication',
    )
}

urls.py

urlpatterns = [
    url(r'^v1/', include([
        url(r'^', include(router.urls)),
        url(r'^auth/', MyAuthentication.as_view()),
        url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
        url(r'^admin/', include(admin.site.urls)),
    ])),
]

views.py

@method_decorator(csrf_exempt, name='dispatch')
class MyAuthentication(TemplateView):

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

        return HttpResponse('Hello, World!')

在此之后,我总是收到 CSRF 验证失败错误。

我在 django-rest-framework 的 IRC 频道中问了这个问题,但我仍然没有答案。请,任何建议将不胜感激。

【问题讨论】:

标签: django python-3.x django-rest-framework django-csrf


【解决方案1】:

不要在 Django REST 框架中使用 csrf_exempt

这不起作用,因为 SessionAuthentication 无论如何都会强制执行 csrf 检查。

请确保您在 AJAX 请求中使用 csrf 令牌。 Django 有关于它的a comprehensive documentation

【讨论】:

  • 正如我在问题中提到的,我需要为 API 应用程序免除 csrf 令牌,以允许其他客户端使用端点。问题不在于 AJAX 请求,而在于如何使会话中间件适用于其他应用程序,即移动设备,而不会将其从现有项目中删除。
  • 那你就搞错了。令牌认证或 OAuth 应该用于非浏览器客户端。
  • 我认为您并没有真正理解这个问题。 API 与执行 AJAX 请求不同。 AJAX 请求用于使用端点,而不是创建 API
  • 其实我不认为你明白我的意思。 API 会响应各种请求,其中一些可能是 AJAX。我的观点是 AJAX 请求应该使用 SessionAuthentication 进行身份验证,而其他机器请求(iOS、Android、curl、wget 或其他)应该使用令牌进行身份验证。请注意,only SessionAuth 需要 CSRF。
  • 对不起,但我做到了,这就是我知道你在这些 cmets 中写的内容返回到同一个问题,根本没有解决它:如何仅对 API 应用程序禁用 CSRF? ,关于我已经找到的答案并将其作为答案发布在同一页面中。
【解决方案2】:

我找到了解决这个问题的方法。您需要创建一个在任何会话中间件之前调用的中间件,然后检查您想要的 url 或应用程序以免除 CSRF 令牌验证。所以,代码应该是这样的:

settings.py

MIDDLEWARE_CLASSES = [
    'api.middleware.DisableCSRF',  # custom middleware for API
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'a9.utils.middleware.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'a9.core.access.middleware.AccessMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
]

urls.py

app_name = "api"

urlpatterns = [
    url(r'^v1/', include([
        url(r'^', include(router.urls)),
        url(r'^auth/', MyAuthentication.as_view()),
        url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
        url(r'^admin/', include(admin.site.urls)),
    ])),
]

csrf_disable.py

from django.core.urlresolvers import resolve
# django2

    
class DisableCSRF(object):
    """Middleware for disabling CSRF in an specified app name.
    """

    def process_request(self, request):
        """Preprocess the request.
        """
        app_name = "api"
        if resolve(request.path_info).app_name == app_name:
            setattr(request, '_dont_enforce_csrf_checks', True)
        else:
            pass  # check CSRF token validation

这只会针对特定的应用程序或 url 检查 CSRF 令牌,而不会删除所有 CSRF。此外,这是独立于 django-rest-framework 的 :)

【讨论】:

    【解决方案3】:

    您需要在dispatch 方法中装饰csrf_exempt

    class MyView(FormView):
    
        @method_decorator(csrf_exempt)
        def dispatch(self, *args, **kwargs):
            return super(MyView, self).dispatch(*args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            # ....
            return super(MyView, self).post(request, *args, **kwargs)
    

    【讨论】:

    • 这就是@method_decorator(csrf_exempt, name='dispatch') 所做的
    【解决方案4】:

    在我的情况下,在 django3.2 中使用 DRF 和基于函数的视图。我必须将permission_classes 明确设置为[]

    @api_view(["GET"])
    @permission_classes([])
    def ping(*args, **kwargs):
        """
        Used to double check that the api is up an running.
        """
        return Response({"msg": "pong"}, status=200)
    
    

    我相信基于类的视图可能是相似的:

    class PingView(ListAPIView):
        permission_classes = []
        pagination_class = None
        serializer_class = None
        def get(self):
            return Response({"msg": "pong"}, status=200)
    

    【讨论】:

      猜你喜欢
      • 2023-03-14
      • 2015-09-03
      • 2015-03-24
      • 2015-02-03
      • 1970-01-01
      • 2012-05-02
      • 2018-08-21
      • 2013-04-06
      相关资源
      最近更新 更多