Django @csrf_exempt 在类视图中不起作用
Posted
技术标签:
【中文标题】Django @csrf_exempt 在类视图中不起作用【英文标题】:Django @csrf_exempt not working in class View 【发布时间】: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 频道中问了这个问题,但我仍然没有答案。请,任何建议将不胜感激。
【问题讨论】:
您是否尝试在类中导入 View 而不是 TemplateView ? 没有可用于 TemplateView 的 post 方法。 是的,这是第一次尝试。实际上,TemplateView 继承自 View,因此它有一个 post 方法:docs.djangoproject.com/en/1.9/ref/class-based-views/base/… 【参考方案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)
【讨论】:
以上是关于Django @csrf_exempt 在类视图中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
csrf_exempt装饰器在基于Dajngo函数的视图中不起作用
@csrf_exempt 在 Django 1.4 中停止工作
为啥使用 django-rest-framework 时不需要 `csrf_exempt`?