django rest框架在POST、PUT、DELETE上返回403响应,尽管权限允许

django rest框架在POST、PUT、DELETE上返回403响应,尽管权限允许,django,authentication,permissions,django-rest-framework,Django,Authentication,Permissions,Django Rest Framework,我正在使用允许在我的站点上进行社交登录会话身份验证。虽然它不是django rest framework的推荐身份验证提供程序之一,rest\u framework.authentication.SessionAuthentication使用django的默认会话身份验证。所以我认为整合起来应该相当简单 在权限方面,我最终将使用IsAdmin,但出于开发目的,我刚刚将其设置为IsAuthenticated。当返回403s时,我将权限放宽到AllowAny,但仍然没有骰子。以下是我的rest框架配

我正在使用允许在我的站点上进行社交登录会话身份验证。虽然它不是django rest framework的推荐身份验证提供程序之一,
rest\u framework.authentication.SessionAuthentication
使用django的默认会话身份验证。所以我认为整合起来应该相当简单

在权限方面,我最终将使用
IsAdmin
,但出于开发目的,我刚刚将其设置为
IsAuthenticated
。当返回403s时,我将权限放宽到
AllowAny
,但仍然没有骰子。以下是我的rest框架配置:

设置.py 编辑:


我根据下面的答案得出了这个结论。原来
rest\u框架
需要
csrftoken
cookie和一个值相同的
X-csrftoken
头,我将前端代码设置为发送所有ajax请求的头,一切正常。

Django REST框架在两种相关情况下返回状态代码
403

  • 当您没有所需的权限级别时(例如,当
    默认权限\u类
    ('rest\u framework.permissions.IsAuthenticated'),以未经验证的用户身份发出API请求)
  • 当您执行不安全的请求类型(POST、PUT、PATCH或DELETE-一个应该有副作用的请求)时,您使用的是
    rest\u framework.authentication.SessionAuthentication
    ,并且您没有将CSRFToken包括在重新请求集中
  • 当您执行不安全的请求类型时,您包含的CSRFToken不再有效
我将针对一个测试API提出几个演示请求,给出每个请求的示例,以帮助您诊断您遇到的问题并说明如何解决它。我将使用
请求

测试API

import json
import requests

response = requests.get('http://localhost:8000/life/1/')
# prints (403, '{"detail":"Authentication credentials were not provided."}')
print response.status_code, response.content

my_session_id = 'mph3eugf0gh5hyzc8glvrt79r2sd6xu6'
cookies = {}
cookies['sessionid'] = my_session_id
response = requests.get('http://localhost:8000/life/1/',
                        cookies=cookies)
# prints (200, '{"id":1,"answer":42}')
print response.status_code, response.content

data = json.dumps({'answer': 24})
headers = {'content-type': 'application/json'}
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF cookie not set."}')
print response.status_code, response.content

# Let's grab a valid csrftoken
html_response = requests.get('http://localhost:8000/life/1/',
                             headers={'accept': 'text/html'},
                             cookies=cookies)
cookies['csrftoken'] = html_response.cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF token missing or incorrect."}')
print response.status_code, response.content

headers['X-CSRFToken'] = cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (200, '{"id":1,"answer":24}')
print response.status_code, response.content
我用一个模型设置了一个非常简单的DRF API,
Life
,它包含一个字段(
answer
,默认值为
42
)。从这里开始,一切都非常简单;我在
/life
URL路由上设置了
模型序列化器
-
生命序列化器
模型视图集
-
生命视图集
,以及
默认路由器
。我已将DRF配置为要求对用户的身份进行身份验证才能使用API和
会话身份验证

点击API

import json
import requests

response = requests.get('http://localhost:8000/life/1/')
# prints (403, '{"detail":"Authentication credentials were not provided."}')
print response.status_code, response.content

my_session_id = 'mph3eugf0gh5hyzc8glvrt79r2sd6xu6'
cookies = {}
cookies['sessionid'] = my_session_id
response = requests.get('http://localhost:8000/life/1/',
                        cookies=cookies)
# prints (200, '{"id":1,"answer":42}')
print response.status_code, response.content

data = json.dumps({'answer': 24})
headers = {'content-type': 'application/json'}
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF cookie not set."}')
print response.status_code, response.content

# Let's grab a valid csrftoken
html_response = requests.get('http://localhost:8000/life/1/',
                             headers={'accept': 'text/html'},
                             cookies=cookies)
cookies['csrftoken'] = html_response.cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (403, '{"detail":"CSRF Failed: CSRF token missing or incorrect."}')
print response.status_code, response.content

headers['X-CSRFToken'] = cookies['csrftoken']
response = requests.put('http://localhost:8000/life/1/',
                        data=data, headers=headers,
                        cookies=cookies)
# prints (200, '{"id":1,"answer":24}')
print response.status_code, response.content

为了完整性起见,还有一种情况下DRF返回代码403:如果您忘记添加
as_view()
添加到您的URL.py文件中的视图声明中。这件事发生在我身上,我花了好几个小时才找到问题所在,因此,这一添加可能会为某些人节省一些时间。

仅适用于可能发现相同问题的任何人。 如果使用不带路由器的视图集,如:

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
Django Rest framework将返回403,除非您在类级别定义权限类:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    permission_classes= YourPermisionClass

希望有帮助!

hmm…我的Ember应用程序确实在请求中传递csrf cookie。我在apache中为我的rest api设置了反向代理。也许我需要修改我的反向代理,以便它将csrf cookie值复制到
HTTP\u X\u CSRFTOKEN
头中?我想我应该将Ember配置为始终传递CSRFTOKEN标题中的cookie值。这应该更容易。@ckot我已经更新了我的答案。忘记了标题
X-CSRFToken
和cookie
CSRFToken
都必须设置。谢谢。太棒了,这很有意义。我稍后会尝试一下,我需要先研究一下配置余烬位。我想这只是一个
$.ajaxSetup()
或其他等效功能。谢谢!你无法想象你为我节省了多少时间。现在我可以使用
IsAuthenticated
privs。一旦我更新了我的社交登录/注册页面,使其能够区分管理员和普通用户,我就可以切换到
IsAdmin
,就像django类的
视图一样基于视图,据我所知,它与django rest framework中的api URL无关。DRF在其路由文档中也没有提及它(包括非DRF视图除外)。您能详细说明一下您的意思吗?