尽管有 AllowAny 权限,django-rest-framework 在 POST、PUT、DELETE 上返回 403 响应
Posted
技术标签:
【中文标题】尽管有 AllowAny 权限,django-rest-framework 在 POST、PUT、DELETE 上返回 403 响应【英文标题】:django-rest-framework returning 403 response on POST, PUT, DELETE despite AllowAny permissions 【发布时间】:2015-12-15 16:35:50 【问题描述】:我使用django-oneall 允许在我的网站上进行社交登录会话身份验证。虽然它不是 django-rest-framework 的建议身份验证提供程序之一,但rest_framework.authentication.SessionAuthentication
使用 django 的默认会话身份验证。所以我认为集成应该相当简单。
在权限方面,最终我将使用IsAdmin
,但出于开发目的,我只是将其设置为IsAuthenticated
。当返回 403 时,我将权限放宽到 AllowAny
,但仍然没有骰子。这是我的休息框架配置:
settings.py
REST_FRAMEWORK =
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
# 'rest_framework.permissions.IsAuthenticated',
# 'rest_framework.permissions.IsAdminUser',
),
'PAGE_SIZE': 100,
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.DjangoFilterBackend',
),
编辑:
我根据下面的答案得到了这个工作。事实证明,rest_framework
需要 csrftoken
cookie 和 X-CSRFToken
相同值的标头,我设置前端代码为所有 ajax 请求发送该标头,一切正常。
【问题讨论】:
【参考方案1】:Django REST Framework 在几种相关情况下返回状态码403
:
DEFAULT_PERMISSION_CLASSES
为 ('rest_framework.permissions.IsAuthenticated',)
时,以未经身份验证的用户身份发出 API 请求。
当您执行不安全的请求类型(POST、PUT、PATCH 或 DELETE - 应该有副作用的请求)时,您使用的是rest_framework.authentication.SessionAuthentication
,并且您没有在请求集中包含您的 CSRFToken。
当您执行不安全的请求类型并且您包含的 CSRFToken 不再有效时。
我将针对测试 API 发出一些演示请求,以提供每个示例,以帮助您诊断遇到的问题并展示如何解决它。我将使用requests
库。
测试 API
我使用单个模型 Life
设置了一个非常简单的 DRF API,其中包含单个字段(answer
,默认值为 42
)。从现在开始的一切都非常简单。我在/life
URL 路由上设置了ModelSerializer
- LifeSerializer
、ModelViewSet
- LifeViewSet
和DefaultRouter
。我已将 DRF 配置为要求用户通过身份验证才能使用 API 并使用 SessionAuthentication
。
使用 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
【讨论】:
hmm... 我的 Ember 应用确实在请求中传递了 csrf cookie。我在 apache 中为我的 rest api 设置了一个反向代理。也许我需要以某种方式修改我的反向代理,以便将 csrf cookie 值复制到HTTP_X_CSRFTOKEN
标头中?
我想我宁愿将 ember 配置为始终在标头中传递 csrftoken cookie 值。这应该更容易。
@ckot 我已经更新了我的答案。忘了标头 X-CSRFToken
和 cookie csrftoken
都必须设置。
谢谢。太棒了,这完全有道理。我会尝试一下,我需要先研究配置 ember 位。我想这只是一个$.ajaxSetup()
或类似的东西
是的。成功了,谢谢!你无法想象你为我节省了多少时间。现在我可以使用IsAuthenticated
privs。一旦我更新了我的社交登录/注册页面,以便它可以区分管理员和普通用户,我就可以切换到IsAdmin
!【参考方案2】:
仅适用于可能发现相同问题的任何人。 如果您使用没有路由器的视图集,例如:
user_list = UserViewSet.as_view('get': 'list')
user_detail = UserViewSet.as_view('get': 'retrieve')
除非您在类级别定义 permission_classes,否则 Django Rest 框架将返回 403:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
permission_classes= YourPermisionClass
希望对你有帮助!
【讨论】:
【参考方案3】:为了完整起见,DRF 在另一种情况下返回代码 403:如果您忘记在 urls.py 文件中的视图声明中添加 as_view()
。刚刚发生在我身上,我花了几个小时才找到问题所在,所以也许这个添加可以为某人节省一些时间。
【讨论】:
as_view
用于基于 django 类的视图,据我所知,它与 django-rest-framework 中的 api-url 无关。 DRF 也没有在他们的路由文档中提到它(除了包括非 DRF 视图)django-rest-framework.org/api-guide/routers。你能详细说明你的意思吗?以上是关于尽管有 AllowAny 权限,django-rest-framework 在 POST、PUT、DELETE 上返回 403 响应的主要内容,如果未能解决你的问题,请参考以下文章
Django Rest Framework 请求对 AllowAny 设置进行身份验证